From cc859548af17b8470679369882af8229190468ad Mon Sep 17 00:00:00 2001 From: Gerrit Date: Mon, 25 Mar 2024 13:21:23 +0100 Subject: [PATCH 01/20] Some firewall machine status fields became optional. --- api/v2/types_firewall.go | 6 +- ...ll.metal-stack.io_firewalldeployments.yaml | 92 ++++++++-------- ...ewall.metal-stack.io_firewallmonitors.yaml | 51 +++++---- .../firewall.metal-stack.io_firewalls.yaml | 103 +++++++++--------- .../firewall.metal-stack.io_firewallsets.yaml | 86 +++++++-------- config/webhooks/manifests.yaml | 26 ++--- go.mod | 37 +++---- go.sum | 79 +++++++------- 8 files changed, 238 insertions(+), 242 deletions(-) diff --git a/api/v2/types_firewall.go b/api/v2/types_firewall.go index 151ab0e..322ce25 100644 --- a/api/v2/types_firewall.go +++ b/api/v2/types_firewall.go @@ -255,9 +255,9 @@ type FirewallNetwork struct { // Asn is the autonomous system number of this network. ASN *int64 `json:"asn"` // DestinationPrefixes are the destination prefixes of this network. - DestinationPrefixes []string `json:"destinationPrefixes"` + DestinationPrefixes []string `json:"destinationPrefixes,omitempty"` // IPs are the ip addresses used in this network. - IPs []string `json:"ips"` + IPs []string `json:"ips,omitempty"` // Nat specifies whether the outgoing traffic is natted or not. Nat *bool `json:"nat"` // NetworkID is the id of this network. @@ -265,7 +265,7 @@ type FirewallNetwork struct { // NetworkType is the type of this network. NetworkType *string `json:"networkType"` // Prefixes are the network prefixes of this network. - Prefixes []string `json:"prefixes"` + Prefixes []string `json:"prefixes,omitempty"` // Vrf is vrf id of this network. Vrf *int64 `json:"vrf"` } diff --git a/config/crds/firewall.metal-stack.io_firewalldeployments.yaml b/config/crds/firewall.metal-stack.io_firewalldeployments.yaml index bdbf4f2..af0ea84 100644 --- a/config/crds/firewall.metal-stack.io_firewalldeployments.yaml +++ b/config/crds/firewall.metal-stack.io_firewalldeployments.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: firewalldeployments.firewall.metal-stack.io spec: group: firewall.metal-stack.io @@ -41,14 +40,19 @@ spec: rolling update for the managed firewalls. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -56,21 +60,22 @@ spec: description: Spec contains the firewall deployment specification. properties: replicas: - description: Replicas is the amount of firewall replicas targeted - to be running. Defaults to 1. + description: |- + Replicas is the amount of firewall replicas targeted to be running. + Defaults to 1. type: integer selector: additionalProperties: type: string - description: Selector is a label query over firewalls that should - match the replicas count. If selector is empty, it is defaulted - to the labels present on the firewall template. Label keys and values - that must match in order to be controlled by this replication controller, - if empty defaulted to labels on firewall template. + description: |- + Selector is a label query over firewalls that should match the replicas count. + If selector is empty, it is defaulted to the labels present on the firewall template. + Label keys and values that must match in order to be controlled by this replication + controller, if empty defaulted to labels on firewall template. type: object strategy: - description: Strategy describes the strategy how firewalls are updated - in case the update requires a physical recreation of the firewalls. + description: |- + Strategy describes the strategy how firewalls are updated in case the update requires a physical recreation of the firewalls. Defaults to RollingUpdate strategy. type: string template: @@ -100,11 +105,10 @@ spec: description: Spec contains the firewall specification. properties: allowedNetworks: - description: AllowedNetworks defines dedicated networks for - which the firewall allows in- and outgoing traffic. The - firewall-controller only enforces this setting in combination - with NetworkAccessType set to forbidden. The node network - is always allowed. + description: |- + AllowedNetworks defines dedicated networks for which the firewall allows in- and outgoing traffic. + The firewall-controller only enforces this setting in combination with NetworkAccessType set to forbidden. + The node network is always allowed. properties: egress: description: Egress defines a list of cidrs which are @@ -161,15 +165,14 @@ spec: type: object type: array image: - description: Image is the os image of the firewall. An update - on this field requires the recreation of the physical firewall - and can therefore lead to traffic interruption for the cluster. + description: |- + Image is the os image of the firewall. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string internalPrefixes: - description: InternalPrefixes specify prefixes which are considered - local to the partition or all regions. This is used for - the traffic counters. Traffic to/from these prefixes is - counted as internal traffic. + description: |- + InternalPrefixes specify prefixes which are considered local to the partition or all regions. This is used for the traffic counters. + Traffic to/from these prefixes is counted as internal traffic. items: type: string type: array @@ -186,12 +189,10 @@ spec: accepted connections in the droptailer log. type: boolean networks: - description: Networks are the networks to which this firewall - is connected. An update on this field requires the recreation - of the physical firewall and can therefore lead to traffic - interruption for the cluster. Detailed information about - the networks are fetched continuously during runtime and - stored in the status.firewallNetworks. + description: |- + Networks are the networks to which this firewall is connected. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. + Detailed information about the networks are fetched continuously during runtime and stored in the status.firewallNetworks. items: type: string type: array @@ -232,24 +233,21 @@ spec: type: object type: array size: - description: Size is the machine size of the firewall. An - update on this field requires the recreation of the physical - firewall and can therefore lead to traffic interruption - for the cluster. + description: |- + Size is the machine size of the firewall. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string sshPublicKeys: - description: SSHPublicKeys are public keys which are added - to the firewall's authorized keys file on creation. It gets - defaulted to the public key of ssh secret as provided by - the controller flags. + description: |- + SSHPublicKeys are public keys which are added to the firewall's authorized keys file on creation. + It gets defaulted to the public key of ssh secret as provided by the controller flags. items: type: string type: array userdata: - description: Userdata contains the userdata used for the creation - of the firewall. It gets defaulted to a userdata matching - for the firewall-controller with connection to Gardener - shoot and seed. + description: |- + Userdata contains the userdata used for the creation of the firewall. + It gets defaulted to a userdata matching for the firewall-controller with connection to Gardener shoot and seed. type: string required: - image diff --git a/config/crds/firewall.metal-stack.io_firewallmonitors.yaml b/config/crds/firewall.metal-stack.io_firewallmonitors.yaml index 438f5b0..c73f36a 100644 --- a/config/crds/firewall.metal-stack.io_firewallmonitors.yaml +++ b/config/crds/firewall.metal-stack.io_firewallmonitors.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: firewallmonitors.firewall.metal-stack.io spec: group: firewall.metal-stack.io @@ -36,14 +35,16 @@ spec: name: v2 schema: openAPIV3Schema: - description: FirewallMonitor is typically deployed into the shoot cluster - in comparison to the other resources of this controller which are deployed - into the seed cluster's shoot namespace. + description: |- + FirewallMonitor is typically deployed into the shoot cluster in comparison to the other resources of this controller + which are deployed into the seed cluster's shoot namespace. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string conditions: description: Conditions contain the latest available observations of a @@ -89,19 +90,18 @@ spec: controllerVersion: type: string distance: - description: FirewallDistance defines the as-path length of firewalls, - influencing how strong they attract network traffic for routing - traffic in and out of the cluster. This is of particular interest - during rolling firewall updates, i.e. when there is more than a - single firewall running in front of the cluster. During a rolling - update, new firewalls start with a longer distance such that traffic - is only attracted by the existing firewalls ("firewall staging"). - When the new firewall has connected successfully to the firewall - monitor, the deployment controller throws away the old firewalls - and the new firewall takes over the routing. The deployment controller - will then shorten the distance of the new firewall. This approach - reduces service interruption of the external user traffic of the - cluster (for firewall-controller versions that support this feature). + description: |- + FirewallDistance defines the as-path length of firewalls, influencing how strong they attract + network traffic for routing traffic in and out of the cluster. + This is of particular interest during rolling firewall updates, i.e. when there is + more than a single firewall running in front of the cluster. + During a rolling update, new firewalls start with a longer distance such that + traffic is only attracted by the existing firewalls ("firewall staging"). + When the new firewall has connected successfully to the firewall monitor, the deployment + controller throws away the old firewalls and the new firewall takes over the routing. + The deployment controller will then shorten the distance of the new firewall. + This approach reduces service interruption of the external user traffic of the cluster + (for firewall-controller versions that support this feature). type: integer distanceSupported: type: boolean @@ -209,9 +209,12 @@ spec: description: Image is the os image of the firewall. type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string logAcceptedConnections: description: LogAcceptedConnections if set to true, also log accepted diff --git a/config/crds/firewall.metal-stack.io_firewalls.yaml b/config/crds/firewall.metal-stack.io_firewalls.yaml index bf2cb43..f0f56f9 100644 --- a/config/crds/firewall.metal-stack.io_firewalls.yaml +++ b/config/crds/firewall.metal-stack.io_firewalls.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: firewalls.firewall.metal-stack.io spec: group: firewall.metal-stack.io @@ -48,18 +47,24 @@ spec: cluster. It has a 1:1 relationship to a firewall in the metal-stack api. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string distance: - description: Distance defines the as-path length of a firewall. This field - is typically orchestrated by the deployment controller. + description: |- + Distance defines the as-path length of a firewall. + This field is typically orchestrated by the deployment controller. type: integer kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -67,10 +72,10 @@ spec: description: Spec contains the firewall specification. properties: allowedNetworks: - description: AllowedNetworks defines dedicated networks for which - the firewall allows in- and outgoing traffic. The firewall-controller - only enforces this setting in combination with NetworkAccessType - set to forbidden. The node network is always allowed. + description: |- + AllowedNetworks defines dedicated networks for which the firewall allows in- and outgoing traffic. + The firewall-controller only enforces this setting in combination with NetworkAccessType set to forbidden. + The node network is always allowed. properties: egress: description: Egress defines a list of cidrs which are allowed @@ -126,15 +131,14 @@ spec: type: object type: array image: - description: Image is the os image of the firewall. An update on this - field requires the recreation of the physical firewall and can therefore - lead to traffic interruption for the cluster. + description: |- + Image is the os image of the firewall. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string internalPrefixes: - description: InternalPrefixes specify prefixes which are considered - local to the partition or all regions. This is used for the traffic - counters. Traffic to/from these prefixes is counted as internal - traffic. + description: |- + InternalPrefixes specify prefixes which are considered local to the partition or all regions. This is used for the traffic counters. + Traffic to/from these prefixes is counted as internal traffic. items: type: string type: array @@ -151,11 +155,10 @@ spec: connections in the droptailer log. type: boolean networks: - description: Networks are the networks to which this firewall is connected. - An update on this field requires the recreation of the physical - firewall and can therefore lead to traffic interruption for the - cluster. Detailed information about the networks are fetched continuously - during runtime and stored in the status.firewallNetworks. + description: |- + Networks are the networks to which this firewall is connected. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. + Detailed information about the networks are fetched continuously during runtime and stored in the status.firewallNetworks. items: type: string type: array @@ -193,21 +196,21 @@ spec: type: object type: array size: - description: Size is the machine size of the firewall. An update on - this field requires the recreation of the physical firewall and - can therefore lead to traffic interruption for the cluster. + description: |- + Size is the machine size of the firewall. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string sshPublicKeys: - description: SSHPublicKeys are public keys which are added to the - firewall's authorized keys file on creation. It gets defaulted to - the public key of ssh secret as provided by the controller flags. + description: |- + SSHPublicKeys are public keys which are added to the firewall's authorized keys file on creation. + It gets defaulted to the public key of ssh secret as provided by the controller flags. items: type: string type: array userdata: - description: Userdata contains the userdata used for the creation - of the firewall. It gets defaulted to a userdata matching for the - firewall-controller with connection to Gardener shoot and seed. + description: |- + Userdata contains the userdata used for the creation of the firewall. + It gets defaulted to a userdata matching for the firewall-controller with connection to Gardener shoot and seed. type: string required: - image @@ -258,11 +261,10 @@ spec: type: object type: array controllerStatus: - description: ControllerStatus holds the a brief version of the firewall-controller - reconciling this firewall. The firewall-controller itself has only - read-access to resources in the seed, including the firewall status - inside the firewall resource. This will be updated by the firewall - monitor controller. + description: |- + ControllerStatus holds the a brief version of the firewall-controller reconciling this firewall. + The firewall-controller itself has only read-access to resources in the seed, including the firewall status + inside the firewall resource. This will be updated by the firewall monitor controller. properties: actualDistance: description: ActualDistance is the actual distance as reflected @@ -284,14 +286,14 @@ spec: type: string type: object firewallNetworks: - description: FirewallNetworks holds refined information about the - networks that this firewall is connected to. The information is - used by the firewall-controller in order to reconcile this firewall. + description: |- + FirewallNetworks holds refined information about the networks that this firewall is connected to. + The information is used by the firewall-controller in order to reconcile this firewall. See .spec.networks. items: - description: FirewallNetwork holds refined information about a network - that the firewall is connected to. The information is used by - the firewall-controller in order to reconcile the firewall. + description: |- + FirewallNetwork holds refined information about a network that the firewall is connected to. + The information is used by the firewall-controller in order to reconcile the firewall. properties: asn: description: Asn is the autonomous system number of this network. @@ -329,12 +331,9 @@ spec: type: integer required: - asn - - destinationPrefixes - - ips - nat - networkID - networkType - - prefixes - vrf type: object type: array @@ -396,9 +395,9 @@ spec: description: APIServerURL is the URL of the shoot's API server. type: string genericKubeconfigSecretName: - description: GenericKubeconfigSecretName is the secret name of - the generic kubeconfig secret deployed by Gardener to be used - as a template for constructing a shoot client. + description: |- + GenericKubeconfigSecretName is the secret name of the generic kubeconfig secret deployed by Gardener + to be used as a template for constructing a shoot client. type: string namespace: description: Namespace is the namespace in the seed where the diff --git a/config/crds/firewall.metal-stack.io_firewallsets.yaml b/config/crds/firewall.metal-stack.io_firewallsets.yaml index e752c9a..ae2878a 100644 --- a/config/crds/firewall.metal-stack.io_firewallsets.yaml +++ b/config/crds/firewall.metal-stack.io_firewallsets.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: firewallsets.firewall.metal-stack.io spec: group: firewall.metal-stack.io @@ -45,14 +44,19 @@ spec: of firewall replicas is running. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -60,7 +64,8 @@ spec: description: Spec contains the firewall set specification. properties: distance: - description: Distance defines the as-path length of the firewalls. + description: |- + Distance defines the as-path length of the firewalls. This field is typically orchestrated by the deployment controller. type: integer replicas: @@ -70,11 +75,11 @@ spec: selector: additionalProperties: type: string - description: Selector is a label query over firewalls that should - match the replicas count. If selector is empty, it is defaulted - to the labels present on the firewall template. Label keys and values - that must match in order to be controlled by this replication controller, - if empty defaulted to labels on firewall template. + description: |- + Selector is a label query over firewalls that should match the replicas count. + If selector is empty, it is defaulted to the labels present on the firewall template. + Label keys and values that must match in order to be controlled by this replication + controller, if empty defaulted to labels on firewall template. type: object template: description: Template is the firewall spec used for creating the firewalls. @@ -103,11 +108,10 @@ spec: description: Spec contains the firewall specification. properties: allowedNetworks: - description: AllowedNetworks defines dedicated networks for - which the firewall allows in- and outgoing traffic. The - firewall-controller only enforces this setting in combination - with NetworkAccessType set to forbidden. The node network - is always allowed. + description: |- + AllowedNetworks defines dedicated networks for which the firewall allows in- and outgoing traffic. + The firewall-controller only enforces this setting in combination with NetworkAccessType set to forbidden. + The node network is always allowed. properties: egress: description: Egress defines a list of cidrs which are @@ -164,15 +168,14 @@ spec: type: object type: array image: - description: Image is the os image of the firewall. An update - on this field requires the recreation of the physical firewall - and can therefore lead to traffic interruption for the cluster. + description: |- + Image is the os image of the firewall. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string internalPrefixes: - description: InternalPrefixes specify prefixes which are considered - local to the partition or all regions. This is used for - the traffic counters. Traffic to/from these prefixes is - counted as internal traffic. + description: |- + InternalPrefixes specify prefixes which are considered local to the partition or all regions. This is used for the traffic counters. + Traffic to/from these prefixes is counted as internal traffic. items: type: string type: array @@ -189,12 +192,10 @@ spec: accepted connections in the droptailer log. type: boolean networks: - description: Networks are the networks to which this firewall - is connected. An update on this field requires the recreation - of the physical firewall and can therefore lead to traffic - interruption for the cluster. Detailed information about - the networks are fetched continuously during runtime and - stored in the status.firewallNetworks. + description: |- + Networks are the networks to which this firewall is connected. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. + Detailed information about the networks are fetched continuously during runtime and stored in the status.firewallNetworks. items: type: string type: array @@ -235,24 +236,21 @@ spec: type: object type: array size: - description: Size is the machine size of the firewall. An - update on this field requires the recreation of the physical - firewall and can therefore lead to traffic interruption - for the cluster. + description: |- + Size is the machine size of the firewall. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string sshPublicKeys: - description: SSHPublicKeys are public keys which are added - to the firewall's authorized keys file on creation. It gets - defaulted to the public key of ssh secret as provided by - the controller flags. + description: |- + SSHPublicKeys are public keys which are added to the firewall's authorized keys file on creation. + It gets defaulted to the public key of ssh secret as provided by the controller flags. items: type: string type: array userdata: - description: Userdata contains the userdata used for the creation - of the firewall. It gets defaulted to a userdata matching - for the firewall-controller with connection to Gardener - shoot and seed. + description: |- + Userdata contains the userdata used for the creation of the firewall. + It gets defaulted to a userdata matching for the firewall-controller with connection to Gardener shoot and seed. type: string required: - image diff --git a/config/webhooks/manifests.yaml b/config/webhooks/manifests.yaml index d37b532..279b136 100644 --- a/config/webhooks/manifests.yaml +++ b/config/webhooks/manifests.yaml @@ -2,7 +2,6 @@ apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: - creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: @@ -30,9 +29,9 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-firewall-metal-stack-io-v2-firewallset + path: /mutate-firewall-metal-stack-io-v2-firewalldeployment failurePolicy: Fail - name: firewallset.metal-stack.io + name: firewalldeployment.metal-stack.io rules: - apiGroups: - firewall.metal-stack.io @@ -41,7 +40,7 @@ webhooks: operations: - CREATE resources: - - firewallsets + - firewalldeployments sideEffects: None - admissionReviewVersions: - v1 @@ -49,9 +48,9 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-firewall-metal-stack-io-v2-firewalldeployment + path: /mutate-firewall-metal-stack-io-v2-firewallset failurePolicy: Fail - name: firewalldeployment.metal-stack.io + name: firewallset.metal-stack.io rules: - apiGroups: - firewall.metal-stack.io @@ -60,13 +59,12 @@ webhooks: operations: - CREATE resources: - - firewalldeployments + - firewallsets sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: - creationTimestamp: null name: validating-webhook-configuration webhooks: - admissionReviewVersions: @@ -95,9 +93,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-firewall-metal-stack-io-v2-firewallset + path: /validate-firewall-metal-stack-io-v2-firewalldeployment failurePolicy: Fail - name: firewallset.metal-stack.io + name: firewalldeployment.metal-stack.io rules: - apiGroups: - firewall.metal-stack.io @@ -107,7 +105,7 @@ webhooks: - CREATE - UPDATE resources: - - firewallsets + - firewalldeployments sideEffects: None - admissionReviewVersions: - v1 @@ -115,9 +113,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-firewall-metal-stack-io-v2-firewalldeployment + path: /validate-firewall-metal-stack-io-v2-firewallset failurePolicy: Fail - name: firewalldeployment.metal-stack.io + name: firewallset.metal-stack.io rules: - apiGroups: - firewall.metal-stack.io @@ -127,5 +125,5 @@ webhooks: - CREATE - UPDATE resources: - - firewalldeployments + - firewallsets sideEffects: None diff --git a/go.mod b/go.mod index 2578ad4..a196837 100644 --- a/go.mod +++ b/go.mod @@ -12,12 +12,12 @@ require ( github.com/metal-stack/metal-go v0.28.1 github.com/metal-stack/metal-lib v0.15.1 github.com/metal-stack/v v1.0.3 - github.com/onsi/ginkgo/v2 v2.16.0 - github.com/onsi/gomega v1.31.1 + github.com/onsi/ginkgo/v2 v2.17.1 + github.com/onsi/gomega v1.32.0 github.com/stretchr/testify v1.9.0 - k8s.io/api v0.28.3 - k8s.io/apimachinery v0.28.3 - k8s.io/client-go v0.28.3 + k8s.io/api v0.29.0 + k8s.io/apimachinery v0.29.0 + k8s.io/client-go v0.29.0 sigs.k8s.io/controller-runtime v0.14.5 ) @@ -40,7 +40,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/emicklei/go-restful/v3 v3.11.2 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/evanphx/json-patch/v5 v5.8.0 // indirect github.com/fatih/color v1.16.0 // indirect github.com/flatcar/ignition v0.36.2 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -83,7 +83,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/metal-stack/security v0.7.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -95,10 +95,10 @@ require ( github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.16.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.10.1 // indirect + github.com/prometheus/client_golang v1.18.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect @@ -117,7 +117,6 @@ require ( go.opentelemetry.io/otel/metric v1.19.0 // indirect go.opentelemetry.io/otel/trace v1.19.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect @@ -137,12 +136,12 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.28.3 // indirect - k8s.io/component-base v0.28.3 // indirect - k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect - k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + k8s.io/apiextensions-apiserver v0.29.0 // indirect + k8s.io/component-base v0.29.0 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 521aa8f..2463a96 100644 --- a/go.sum +++ b/go.sum @@ -60,8 +60,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= -github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch/v5 v5.8.0 h1:lRj6N9Nci7MvzrXuX6HFzU8XjmhPiXPlsKEy1u0KQro= +github.com/evanphx/json-patch/v5 v5.8.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/flatcar/container-linux-config-transpiler v0.9.4 h1:yXQ0NB8PeNrKJPrZvbv5/DV63PNhTqt8vaf8YxmX/RA= @@ -77,8 +77,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -154,6 +154,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -182,7 +183,6 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -225,8 +225,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/metal-stack/metal-go v0.28.1 h1:fn/mMmcXLMGOkVLNec4dFP/oserzkeYr/00zDcumTmU= github.com/metal-stack/metal-go v0.28.1/go.mod h1:Iw4xnzbtcn3qz7YaK0ekCAcLZUyz5E7e0ZCvJ5pX0gU= github.com/metal-stack/metal-lib v0.15.1 h1:QCmtZ6ci6pHsf3RQnSDbbvYshpyRaxCSeXghVvbDFuA= @@ -248,31 +248,30 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM= -github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= -github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= -github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= +github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= +github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= +github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pin/tftp v2.1.0+incompatible/go.mod h1:xVpZOMCXTy+A5QMjEVN0Glwa1sUvaJhFXbr/aAxuxGY= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= -github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -338,16 +337,18 @@ go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE= -go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go4.org v0.0.0-20160314031811-03efcb870d84/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= @@ -579,20 +580,20 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU= k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE= -k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= -k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= -k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= -k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= +k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0= +k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= +k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= +k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis= k8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s= k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= -k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= -k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/component-base v0.29.0 h1:T7rjd5wvLnPBV1vC4zWd/iWRbV8Mdxs+nGaoaFzGw3s= +k8s.io/component-base v0.29.0/go.mod h1:sADonFTQ9Zc9yFLghpDpmNXEdHyQmFIGbiuZbqAXQ1M= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 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/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= @@ -600,7 +601,7 @@ sigs.k8s.io/controller-runtime v0.14.5 h1:6xaWFqzT5KuAQ9ufgUaj1G/+C4Y1GRkhrxl+BJ sigs.k8s.io/controller-runtime v0.14.5/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= From 755b719bd93fa9b72bb0cd909bc0bc74c56e94a8 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 26 Mar 2024 10:43:13 +0100 Subject: [PATCH 02/20] Add annotation to trigger directed resource reconciliation. --- api/v2/types_utils.go | 39 ++++++- ...ll.metal-stack.io_firewalldeployments.yaml | 92 ++++++++-------- ...ewall.metal-stack.io_firewallmonitors.yaml | 51 ++++----- .../firewall.metal-stack.io_firewalls.yaml | 100 +++++++++--------- .../firewall.metal-stack.io_firewallsets.yaml | 86 ++++++++------- config/webhooks/manifests.yaml | 26 +++-- controllers/deployment/reconcile.go | 13 ++- controllers/firewall/reconcile.go | 11 ++ controllers/monitor/controller.go | 2 +- controllers/monitor/reconcile.go | 68 ++++++------ controllers/set/controller_test.go | 20 ++++ controllers/set/reconcile.go | 11 ++ 12 files changed, 301 insertions(+), 218 deletions(-) diff --git a/api/v2/types_utils.go b/api/v2/types_utils.go index 42130e7..5b68dbd 100644 --- a/api/v2/types_utils.go +++ b/api/v2/types_utils.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "strconv" "github.com/google/go-cmp/cmp" @@ -11,8 +12,14 @@ import ( ) const ( - FinalizerName = "firewall.metal-stack.io/firewall-controller-manager" - RollSetAnnotation = "firewall.metal-stack.io/roll-set" + // FinalizerName is the finalizer name used by this controller. + FinalizerName = "firewall.metal-stack.io/firewall-controller-manager" + // ReconcileAnnotation can be used to trigger a reconciliation of a resource managed by a controller. + // The value of the annotation does not matter, the controller will cleanup the annotation automatically and trigger a reconciliation of the resource. + ReconcileAnnotation = "firewall.metal-stack.io/reconcile" + // RollSetAnnotation can be used to trigger a rolling update of a firewall deployment. + RollSetAnnotation = "firewall.metal-stack.io/roll-set" + // RevisionAnnotation stores the revision number of a resource. RevisionAnnotation = "firewall.metal-stack.io/revision" ) @@ -105,12 +112,34 @@ func (cs Conditions) filterOutCondition(t ConditionType) Conditions { return newConditions } -// SkipReconcileAnnotationRemoval returns a predicate when the firewall controller reconcile annotation +// RemoveAnnotation removes an annotation by a given key from an object if present by updating it with the given client. +// It returns true when the annotation was present and removed and an error if the update process went wrong. +func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key string) (bool, error) { + annotations := o.GetAnnotations() + + _, ok := annotations[key] + if !ok { + return false, nil + } + + delete(annotations, key) + + o.SetAnnotations(annotations) + + err := c.Update(ctx, o) + if err != nil { + return false, err + } + + return true, nil +} + +// SkipAnnotationRemoval returns a predicate when the firewall controller annotation // was cleaned up. -func SkipRollSetAnnotationRemoval() predicate.Funcs { +func SkipAnnotationRemoval(annotation string) predicate.Funcs { return predicate.Funcs{ UpdateFunc: func(update event.UpdateEvent) bool { - return !annotationWasRemoved(update, RollSetAnnotation) + return !annotationWasRemoved(update, annotation) }, } } diff --git a/config/crds/firewall.metal-stack.io_firewalldeployments.yaml b/config/crds/firewall.metal-stack.io_firewalldeployments.yaml index bdbf4f2..af0ea84 100644 --- a/config/crds/firewall.metal-stack.io_firewalldeployments.yaml +++ b/config/crds/firewall.metal-stack.io_firewalldeployments.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: firewalldeployments.firewall.metal-stack.io spec: group: firewall.metal-stack.io @@ -41,14 +40,19 @@ spec: rolling update for the managed firewalls. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -56,21 +60,22 @@ spec: description: Spec contains the firewall deployment specification. properties: replicas: - description: Replicas is the amount of firewall replicas targeted - to be running. Defaults to 1. + description: |- + Replicas is the amount of firewall replicas targeted to be running. + Defaults to 1. type: integer selector: additionalProperties: type: string - description: Selector is a label query over firewalls that should - match the replicas count. If selector is empty, it is defaulted - to the labels present on the firewall template. Label keys and values - that must match in order to be controlled by this replication controller, - if empty defaulted to labels on firewall template. + description: |- + Selector is a label query over firewalls that should match the replicas count. + If selector is empty, it is defaulted to the labels present on the firewall template. + Label keys and values that must match in order to be controlled by this replication + controller, if empty defaulted to labels on firewall template. type: object strategy: - description: Strategy describes the strategy how firewalls are updated - in case the update requires a physical recreation of the firewalls. + description: |- + Strategy describes the strategy how firewalls are updated in case the update requires a physical recreation of the firewalls. Defaults to RollingUpdate strategy. type: string template: @@ -100,11 +105,10 @@ spec: description: Spec contains the firewall specification. properties: allowedNetworks: - description: AllowedNetworks defines dedicated networks for - which the firewall allows in- and outgoing traffic. The - firewall-controller only enforces this setting in combination - with NetworkAccessType set to forbidden. The node network - is always allowed. + description: |- + AllowedNetworks defines dedicated networks for which the firewall allows in- and outgoing traffic. + The firewall-controller only enforces this setting in combination with NetworkAccessType set to forbidden. + The node network is always allowed. properties: egress: description: Egress defines a list of cidrs which are @@ -161,15 +165,14 @@ spec: type: object type: array image: - description: Image is the os image of the firewall. An update - on this field requires the recreation of the physical firewall - and can therefore lead to traffic interruption for the cluster. + description: |- + Image is the os image of the firewall. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string internalPrefixes: - description: InternalPrefixes specify prefixes which are considered - local to the partition or all regions. This is used for - the traffic counters. Traffic to/from these prefixes is - counted as internal traffic. + description: |- + InternalPrefixes specify prefixes which are considered local to the partition or all regions. This is used for the traffic counters. + Traffic to/from these prefixes is counted as internal traffic. items: type: string type: array @@ -186,12 +189,10 @@ spec: accepted connections in the droptailer log. type: boolean networks: - description: Networks are the networks to which this firewall - is connected. An update on this field requires the recreation - of the physical firewall and can therefore lead to traffic - interruption for the cluster. Detailed information about - the networks are fetched continuously during runtime and - stored in the status.firewallNetworks. + description: |- + Networks are the networks to which this firewall is connected. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. + Detailed information about the networks are fetched continuously during runtime and stored in the status.firewallNetworks. items: type: string type: array @@ -232,24 +233,21 @@ spec: type: object type: array size: - description: Size is the machine size of the firewall. An - update on this field requires the recreation of the physical - firewall and can therefore lead to traffic interruption - for the cluster. + description: |- + Size is the machine size of the firewall. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string sshPublicKeys: - description: SSHPublicKeys are public keys which are added - to the firewall's authorized keys file on creation. It gets - defaulted to the public key of ssh secret as provided by - the controller flags. + description: |- + SSHPublicKeys are public keys which are added to the firewall's authorized keys file on creation. + It gets defaulted to the public key of ssh secret as provided by the controller flags. items: type: string type: array userdata: - description: Userdata contains the userdata used for the creation - of the firewall. It gets defaulted to a userdata matching - for the firewall-controller with connection to Gardener - shoot and seed. + description: |- + Userdata contains the userdata used for the creation of the firewall. + It gets defaulted to a userdata matching for the firewall-controller with connection to Gardener shoot and seed. type: string required: - image diff --git a/config/crds/firewall.metal-stack.io_firewallmonitors.yaml b/config/crds/firewall.metal-stack.io_firewallmonitors.yaml index 438f5b0..c73f36a 100644 --- a/config/crds/firewall.metal-stack.io_firewallmonitors.yaml +++ b/config/crds/firewall.metal-stack.io_firewallmonitors.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: firewallmonitors.firewall.metal-stack.io spec: group: firewall.metal-stack.io @@ -36,14 +35,16 @@ spec: name: v2 schema: openAPIV3Schema: - description: FirewallMonitor is typically deployed into the shoot cluster - in comparison to the other resources of this controller which are deployed - into the seed cluster's shoot namespace. + description: |- + FirewallMonitor is typically deployed into the shoot cluster in comparison to the other resources of this controller + which are deployed into the seed cluster's shoot namespace. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string conditions: description: Conditions contain the latest available observations of a @@ -89,19 +90,18 @@ spec: controllerVersion: type: string distance: - description: FirewallDistance defines the as-path length of firewalls, - influencing how strong they attract network traffic for routing - traffic in and out of the cluster. This is of particular interest - during rolling firewall updates, i.e. when there is more than a - single firewall running in front of the cluster. During a rolling - update, new firewalls start with a longer distance such that traffic - is only attracted by the existing firewalls ("firewall staging"). - When the new firewall has connected successfully to the firewall - monitor, the deployment controller throws away the old firewalls - and the new firewall takes over the routing. The deployment controller - will then shorten the distance of the new firewall. This approach - reduces service interruption of the external user traffic of the - cluster (for firewall-controller versions that support this feature). + description: |- + FirewallDistance defines the as-path length of firewalls, influencing how strong they attract + network traffic for routing traffic in and out of the cluster. + This is of particular interest during rolling firewall updates, i.e. when there is + more than a single firewall running in front of the cluster. + During a rolling update, new firewalls start with a longer distance such that + traffic is only attracted by the existing firewalls ("firewall staging"). + When the new firewall has connected successfully to the firewall monitor, the deployment + controller throws away the old firewalls and the new firewall takes over the routing. + The deployment controller will then shorten the distance of the new firewall. + This approach reduces service interruption of the external user traffic of the cluster + (for firewall-controller versions that support this feature). type: integer distanceSupported: type: boolean @@ -209,9 +209,12 @@ spec: description: Image is the os image of the firewall. type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string logAcceptedConnections: description: LogAcceptedConnections if set to true, also log accepted diff --git a/config/crds/firewall.metal-stack.io_firewalls.yaml b/config/crds/firewall.metal-stack.io_firewalls.yaml index bf2cb43..d15fd08 100644 --- a/config/crds/firewall.metal-stack.io_firewalls.yaml +++ b/config/crds/firewall.metal-stack.io_firewalls.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: firewalls.firewall.metal-stack.io spec: group: firewall.metal-stack.io @@ -48,18 +47,24 @@ spec: cluster. It has a 1:1 relationship to a firewall in the metal-stack api. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string distance: - description: Distance defines the as-path length of a firewall. This field - is typically orchestrated by the deployment controller. + description: |- + Distance defines the as-path length of a firewall. + This field is typically orchestrated by the deployment controller. type: integer kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -67,10 +72,10 @@ spec: description: Spec contains the firewall specification. properties: allowedNetworks: - description: AllowedNetworks defines dedicated networks for which - the firewall allows in- and outgoing traffic. The firewall-controller - only enforces this setting in combination with NetworkAccessType - set to forbidden. The node network is always allowed. + description: |- + AllowedNetworks defines dedicated networks for which the firewall allows in- and outgoing traffic. + The firewall-controller only enforces this setting in combination with NetworkAccessType set to forbidden. + The node network is always allowed. properties: egress: description: Egress defines a list of cidrs which are allowed @@ -126,15 +131,14 @@ spec: type: object type: array image: - description: Image is the os image of the firewall. An update on this - field requires the recreation of the physical firewall and can therefore - lead to traffic interruption for the cluster. + description: |- + Image is the os image of the firewall. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string internalPrefixes: - description: InternalPrefixes specify prefixes which are considered - local to the partition or all regions. This is used for the traffic - counters. Traffic to/from these prefixes is counted as internal - traffic. + description: |- + InternalPrefixes specify prefixes which are considered local to the partition or all regions. This is used for the traffic counters. + Traffic to/from these prefixes is counted as internal traffic. items: type: string type: array @@ -151,11 +155,10 @@ spec: connections in the droptailer log. type: boolean networks: - description: Networks are the networks to which this firewall is connected. - An update on this field requires the recreation of the physical - firewall and can therefore lead to traffic interruption for the - cluster. Detailed information about the networks are fetched continuously - during runtime and stored in the status.firewallNetworks. + description: |- + Networks are the networks to which this firewall is connected. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. + Detailed information about the networks are fetched continuously during runtime and stored in the status.firewallNetworks. items: type: string type: array @@ -193,21 +196,21 @@ spec: type: object type: array size: - description: Size is the machine size of the firewall. An update on - this field requires the recreation of the physical firewall and - can therefore lead to traffic interruption for the cluster. + description: |- + Size is the machine size of the firewall. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string sshPublicKeys: - description: SSHPublicKeys are public keys which are added to the - firewall's authorized keys file on creation. It gets defaulted to - the public key of ssh secret as provided by the controller flags. + description: |- + SSHPublicKeys are public keys which are added to the firewall's authorized keys file on creation. + It gets defaulted to the public key of ssh secret as provided by the controller flags. items: type: string type: array userdata: - description: Userdata contains the userdata used for the creation - of the firewall. It gets defaulted to a userdata matching for the - firewall-controller with connection to Gardener shoot and seed. + description: |- + Userdata contains the userdata used for the creation of the firewall. + It gets defaulted to a userdata matching for the firewall-controller with connection to Gardener shoot and seed. type: string required: - image @@ -258,11 +261,10 @@ spec: type: object type: array controllerStatus: - description: ControllerStatus holds the a brief version of the firewall-controller - reconciling this firewall. The firewall-controller itself has only - read-access to resources in the seed, including the firewall status - inside the firewall resource. This will be updated by the firewall - monitor controller. + description: |- + ControllerStatus holds the a brief version of the firewall-controller reconciling this firewall. + The firewall-controller itself has only read-access to resources in the seed, including the firewall status + inside the firewall resource. This will be updated by the firewall monitor controller. properties: actualDistance: description: ActualDistance is the actual distance as reflected @@ -284,14 +286,14 @@ spec: type: string type: object firewallNetworks: - description: FirewallNetworks holds refined information about the - networks that this firewall is connected to. The information is - used by the firewall-controller in order to reconcile this firewall. + description: |- + FirewallNetworks holds refined information about the networks that this firewall is connected to. + The information is used by the firewall-controller in order to reconcile this firewall. See .spec.networks. items: - description: FirewallNetwork holds refined information about a network - that the firewall is connected to. The information is used by - the firewall-controller in order to reconcile the firewall. + description: |- + FirewallNetwork holds refined information about a network that the firewall is connected to. + The information is used by the firewall-controller in order to reconcile the firewall. properties: asn: description: Asn is the autonomous system number of this network. @@ -396,9 +398,9 @@ spec: description: APIServerURL is the URL of the shoot's API server. type: string genericKubeconfigSecretName: - description: GenericKubeconfigSecretName is the secret name of - the generic kubeconfig secret deployed by Gardener to be used - as a template for constructing a shoot client. + description: |- + GenericKubeconfigSecretName is the secret name of the generic kubeconfig secret deployed by Gardener + to be used as a template for constructing a shoot client. type: string namespace: description: Namespace is the namespace in the seed where the diff --git a/config/crds/firewall.metal-stack.io_firewallsets.yaml b/config/crds/firewall.metal-stack.io_firewallsets.yaml index e752c9a..ae2878a 100644 --- a/config/crds/firewall.metal-stack.io_firewallsets.yaml +++ b/config/crds/firewall.metal-stack.io_firewallsets.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: firewallsets.firewall.metal-stack.io spec: group: firewall.metal-stack.io @@ -45,14 +44,19 @@ spec: of firewall replicas is running. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -60,7 +64,8 @@ spec: description: Spec contains the firewall set specification. properties: distance: - description: Distance defines the as-path length of the firewalls. + description: |- + Distance defines the as-path length of the firewalls. This field is typically orchestrated by the deployment controller. type: integer replicas: @@ -70,11 +75,11 @@ spec: selector: additionalProperties: type: string - description: Selector is a label query over firewalls that should - match the replicas count. If selector is empty, it is defaulted - to the labels present on the firewall template. Label keys and values - that must match in order to be controlled by this replication controller, - if empty defaulted to labels on firewall template. + description: |- + Selector is a label query over firewalls that should match the replicas count. + If selector is empty, it is defaulted to the labels present on the firewall template. + Label keys and values that must match in order to be controlled by this replication + controller, if empty defaulted to labels on firewall template. type: object template: description: Template is the firewall spec used for creating the firewalls. @@ -103,11 +108,10 @@ spec: description: Spec contains the firewall specification. properties: allowedNetworks: - description: AllowedNetworks defines dedicated networks for - which the firewall allows in- and outgoing traffic. The - firewall-controller only enforces this setting in combination - with NetworkAccessType set to forbidden. The node network - is always allowed. + description: |- + AllowedNetworks defines dedicated networks for which the firewall allows in- and outgoing traffic. + The firewall-controller only enforces this setting in combination with NetworkAccessType set to forbidden. + The node network is always allowed. properties: egress: description: Egress defines a list of cidrs which are @@ -164,15 +168,14 @@ spec: type: object type: array image: - description: Image is the os image of the firewall. An update - on this field requires the recreation of the physical firewall - and can therefore lead to traffic interruption for the cluster. + description: |- + Image is the os image of the firewall. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string internalPrefixes: - description: InternalPrefixes specify prefixes which are considered - local to the partition or all regions. This is used for - the traffic counters. Traffic to/from these prefixes is - counted as internal traffic. + description: |- + InternalPrefixes specify prefixes which are considered local to the partition or all regions. This is used for the traffic counters. + Traffic to/from these prefixes is counted as internal traffic. items: type: string type: array @@ -189,12 +192,10 @@ spec: accepted connections in the droptailer log. type: boolean networks: - description: Networks are the networks to which this firewall - is connected. An update on this field requires the recreation - of the physical firewall and can therefore lead to traffic - interruption for the cluster. Detailed information about - the networks are fetched continuously during runtime and - stored in the status.firewallNetworks. + description: |- + Networks are the networks to which this firewall is connected. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. + Detailed information about the networks are fetched continuously during runtime and stored in the status.firewallNetworks. items: type: string type: array @@ -235,24 +236,21 @@ spec: type: object type: array size: - description: Size is the machine size of the firewall. An - update on this field requires the recreation of the physical - firewall and can therefore lead to traffic interruption - for the cluster. + description: |- + Size is the machine size of the firewall. + An update on this field requires the recreation of the physical firewall and can therefore lead to traffic interruption for the cluster. type: string sshPublicKeys: - description: SSHPublicKeys are public keys which are added - to the firewall's authorized keys file on creation. It gets - defaulted to the public key of ssh secret as provided by - the controller flags. + description: |- + SSHPublicKeys are public keys which are added to the firewall's authorized keys file on creation. + It gets defaulted to the public key of ssh secret as provided by the controller flags. items: type: string type: array userdata: - description: Userdata contains the userdata used for the creation - of the firewall. It gets defaulted to a userdata matching - for the firewall-controller with connection to Gardener - shoot and seed. + description: |- + Userdata contains the userdata used for the creation of the firewall. + It gets defaulted to a userdata matching for the firewall-controller with connection to Gardener shoot and seed. type: string required: - image diff --git a/config/webhooks/manifests.yaml b/config/webhooks/manifests.yaml index d37b532..279b136 100644 --- a/config/webhooks/manifests.yaml +++ b/config/webhooks/manifests.yaml @@ -2,7 +2,6 @@ apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: - creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: @@ -30,9 +29,9 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-firewall-metal-stack-io-v2-firewallset + path: /mutate-firewall-metal-stack-io-v2-firewalldeployment failurePolicy: Fail - name: firewallset.metal-stack.io + name: firewalldeployment.metal-stack.io rules: - apiGroups: - firewall.metal-stack.io @@ -41,7 +40,7 @@ webhooks: operations: - CREATE resources: - - firewallsets + - firewalldeployments sideEffects: None - admissionReviewVersions: - v1 @@ -49,9 +48,9 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-firewall-metal-stack-io-v2-firewalldeployment + path: /mutate-firewall-metal-stack-io-v2-firewallset failurePolicy: Fail - name: firewalldeployment.metal-stack.io + name: firewallset.metal-stack.io rules: - apiGroups: - firewall.metal-stack.io @@ -60,13 +59,12 @@ webhooks: operations: - CREATE resources: - - firewalldeployments + - firewallsets sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: - creationTimestamp: null name: validating-webhook-configuration webhooks: - admissionReviewVersions: @@ -95,9 +93,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-firewall-metal-stack-io-v2-firewallset + path: /validate-firewall-metal-stack-io-v2-firewalldeployment failurePolicy: Fail - name: firewallset.metal-stack.io + name: firewalldeployment.metal-stack.io rules: - apiGroups: - firewall.metal-stack.io @@ -107,7 +105,7 @@ webhooks: - CREATE - UPDATE resources: - - firewallsets + - firewalldeployments sideEffects: None - admissionReviewVersions: - v1 @@ -115,9 +113,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-firewall-metal-stack-io-v2-firewalldeployment + path: /validate-firewall-metal-stack-io-v2-firewallset failurePolicy: Fail - name: firewalldeployment.metal-stack.io + name: firewallset.metal-stack.io rules: - apiGroups: - firewall.metal-stack.io @@ -127,5 +125,5 @@ webhooks: - CREATE - UPDATE resources: - - firewalldeployments + - firewallsets sideEffects: None diff --git a/controllers/deployment/reconcile.go b/controllers/deployment/reconcile.go index 52289fb..fa85ee7 100644 --- a/controllers/deployment/reconcile.go +++ b/controllers/deployment/reconcile.go @@ -17,7 +17,18 @@ import ( ) func (c *controller) Reconcile(r *controllers.Ctx[*v2.FirewallDeployment]) error { - err := c.ensureFirewallControllerRBAC(r) + wasPresent, err := v2.RemoveAnnotation(r.Ctx, c.c.GetSeedClient(), r.Target, v2.ReconcileAnnotation) + if err != nil { + return err + } + + if wasPresent { + // the update of the annotation removal triggers the next reconciliation + c.log.Info("removed reconcile annotation from resource") + return nil + } + + err = c.ensureFirewallControllerRBAC(r) if err != nil { return err } diff --git a/controllers/firewall/reconcile.go b/controllers/firewall/reconcile.go index 89c07bc..1389661 100644 --- a/controllers/firewall/reconcile.go +++ b/controllers/firewall/reconcile.go @@ -18,6 +18,17 @@ import ( // Reconciler must always return either an error or requeue to ensure that it detects if a firewall get lost etc. func (c *controller) Reconcile(r *controllers.Ctx[*v2.Firewall]) error { + wasPresent, err := v2.RemoveAnnotation(r.Ctx, c.c.GetSeedClient(), r.Target, v2.ReconcileAnnotation) + if err != nil { + return err + } + + if wasPresent { + // the update of the annotation removal triggers the next reconciliation + c.log.Info("removed reconcile annotation from resource") + return nil + } + var f *models.V1FirewallResponse defer func() { if err := c.setStatus(r, f); err != nil { diff --git a/controllers/monitor/controller.go b/controllers/monitor/controller.go index ed37217..4b1ccf4 100644 --- a/controllers/monitor/controller.go +++ b/controllers/monitor/controller.go @@ -25,7 +25,7 @@ func SetupWithManager(log logr.Logger, mgr ctrl.Manager, c *config.ControllerCon For(&v2.FirewallMonitor{}). Named("FirewallMonitor"). WithEventFilter(predicate.NewPredicateFuncs(controllers.SkipOtherNamespace(c.GetShootNamespace()))). - WithEventFilter(v2.SkipRollSetAnnotationRemoval()). + WithEventFilter(v2.SkipAnnotationRemoval(v2.RollSetAnnotation)). Complete(g) } diff --git a/controllers/monitor/reconcile.go b/controllers/monitor/reconcile.go index 3583881..926fa6e 100644 --- a/controllers/monitor/reconcile.go +++ b/controllers/monitor/reconcile.go @@ -16,7 +16,18 @@ import ( ) func (c *controller) Reconcile(r *controllers.Ctx[*v2.FirewallMonitor]) error { - _, err := c.updateFirewallStatus(r) + wasPresent, err := v2.RemoveAnnotation(r.Ctx, c.c.GetShootClient(), r.Target, v2.ReconcileAnnotation) + if err != nil { + return err + } + + if wasPresent { + // the update of the annotation removal triggers the next reconciliation + c.log.Info("removed reconcile annotation from resource") + return nil + } + + _, err = c.updateFirewallStatus(r) if err != nil { r.Log.Error(err, "unable to update firewall status") return controllers.RequeueAfter(3*time.Second, "unable to update firewall status, retrying") @@ -54,53 +65,44 @@ func (c *controller) updateFirewallStatus(r *controllers.Ctx[*v2.FirewallMonitor } func (c *controller) rollSetAnnotation(r *controllers.Ctx[*v2.FirewallMonitor]) error { - v, ok := r.Target.Annotations[v2.RollSetAnnotation] - if !ok { - return nil - } - - r.Log.Info("resource was annotated", "annotation", v2.RollSetAnnotation, "value", v) + rollSet := v2.IsAnnotationTrue(r.Target, v2.RollSetAnnotation) - delete(r.Target.Annotations, v2.RollSetAnnotation) - - err := c.c.GetShootClient().Update(r.Ctx, r.Target) + present, err := v2.RemoveAnnotation(r.Ctx, c.c.GetShootClient(), r.Target, v2.RollSetAnnotation) if err != nil { return err } - r.Log.Info("cleaned up annotation") - - rollSet, err := strconv.ParseBool(v) - if err != nil { - r.Log.Error(err, "unable to parse annotation value, ignoring") + if !present { return nil } - if rollSet { - r.Log.Info("initiating firewall set roll as requested by user annotation") + if !rollSet { + return nil + } - fw := &v2.Firewall{ - ObjectMeta: metav1.ObjectMeta{ - Name: r.Target.Name, - Namespace: c.c.GetSeedNamespace(), - }, - } + r.Log.Info("initiating firewall set roll as requested by user annotation") - set, err := findCorrespondingSet(r.Ctx, c.c.GetSeedClient(), fw) - if err != nil { - return client.IgnoreNotFound(err) - } + fw := &v2.Firewall{ + ObjectMeta: metav1.ObjectMeta{ + Name: r.Target.Name, + Namespace: c.c.GetSeedNamespace(), + }, + } - set.Annotations[v2.RollSetAnnotation] = strconv.FormatBool(true) + set, err := findCorrespondingSet(r.Ctx, c.c.GetSeedClient(), fw) + if err != nil { + return client.IgnoreNotFound(err) + } - err = c.c.GetSeedClient().Update(r.Ctx, set) - if err != nil { - return fmt.Errorf("unable to annotate firewall set: %w", err) - } + set.Annotations[v2.RollSetAnnotation] = strconv.FormatBool(true) - r.Log.Info("firewall set annotated") + err = c.c.GetSeedClient().Update(r.Ctx, set) + if err != nil { + return fmt.Errorf("unable to annotate firewall set: %w", err) } + r.Log.Info("firewall set annotated") + return nil } diff --git a/controllers/set/controller_test.go b/controllers/set/controller_test.go index 13a3bce..f142d9a 100644 --- a/controllers/set/controller_test.go +++ b/controllers/set/controller_test.go @@ -2,6 +2,7 @@ package set_test import ( "fmt" + "strconv" "time" v2 "github.com/metal-stack/firewall-controller-manager/api/v2" @@ -146,4 +147,23 @@ var _ = Context("firewall set controller", Ordered, func() { Expect(k8sClient.Update(ctx, set)).NotTo(Succeed()) }) }) + + Describe("reconcile annotation", Ordered, func() { + It("the annotation can be added", func() { + Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(set), set)).To(Succeed()) + if set.Annotations == nil { + set.Annotations = map[string]string{} + } + set.Annotations[v2.ReconcileAnnotation] = strconv.FormatBool(true) + Expect(k8sClient.Update(ctx, set)).To(Succeed()) + }) + + It("the annotation was cleaned up again", func() { + Eventually(func() bool { + Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(set), set)).To(Succeed()) + _, present := set.Annotations[v2.ReconcileAnnotation] + return present + }, "50ms").Should(BeFalse()) + }) + }) }) diff --git a/controllers/set/reconcile.go b/controllers/set/reconcile.go index 6591ea3..459c5bb 100644 --- a/controllers/set/reconcile.go +++ b/controllers/set/reconcile.go @@ -12,6 +12,17 @@ import ( ) func (c *controller) Reconcile(r *controllers.Ctx[*v2.FirewallSet]) error { + wasPresent, err := v2.RemoveAnnotation(r.Ctx, c.c.GetSeedClient(), r.Target, v2.ReconcileAnnotation) + if err != nil { + return err + } + + if wasPresent { + // the update of the annotation removal triggers the next reconciliation + c.log.Info("removed reconcile annotation from resource") + return nil + } + ownedFirewalls, orphaned, err := controllers.GetOwnedResources(r.Ctx, c.c.GetSeedClient(), r.Target.Spec.Selector, r.Target, &v2.FirewallList{}, func(fl *v2.FirewallList) []*v2.Firewall { return fl.GetItems() }) From e8726fa07713737b1d71e2943956b71394b71676 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 26 Mar 2024 10:46:02 +0100 Subject: [PATCH 03/20] Better comment. --- api/v2/types_utils.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/v2/types_utils.go b/api/v2/types_utils.go index 5b68dbd..a1cd83a 100644 --- a/api/v2/types_utils.go +++ b/api/v2/types_utils.go @@ -134,8 +134,7 @@ func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key return true, nil } -// SkipAnnotationRemoval returns a predicate when the firewall controller annotation -// was cleaned up. +// SkipAnnotationRemoval returns a predicate when the given annotation key was cleaned up. func SkipAnnotationRemoval(annotation string) predicate.Funcs { return predicate.Funcs{ UpdateFunc: func(update event.UpdateEvent) bool { From 426a6c11a250c913ed96b535e7168ca4c073a795 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 26 Mar 2024 10:51:32 +0100 Subject: [PATCH 04/20] Update. --- go.mod | 46 +++++++++++++-------------- go.sum | 98 ++++++++++++++++++++++++++++------------------------------ 2 files changed, 71 insertions(+), 73 deletions(-) diff --git a/go.mod b/go.mod index 2578ad4..5a8c7ee 100644 --- a/go.mod +++ b/go.mod @@ -10,15 +10,15 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/metal-stack/metal-go v0.28.1 - github.com/metal-stack/metal-lib v0.15.1 + github.com/metal-stack/metal-lib v0.16.0 github.com/metal-stack/v v1.0.3 - github.com/onsi/ginkgo/v2 v2.16.0 - github.com/onsi/gomega v1.31.1 + github.com/onsi/ginkgo/v2 v2.17.1 + github.com/onsi/gomega v1.32.0 github.com/stretchr/testify v1.9.0 k8s.io/api v0.28.3 k8s.io/apimachinery v0.28.3 k8s.io/client-go v0.28.3 - sigs.k8s.io/controller-runtime v0.14.5 + sigs.k8s.io/controller-runtime v0.14.7 ) replace ( @@ -33,28 +33,28 @@ require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/coreos/go-oidc/v3 v3.9.0 // indirect + github.com/coreos/go-oidc/v3 v3.10.0 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/emicklei/go-restful/v3 v3.11.2 // indirect + github.com/emicklei/go-restful/v3 v3.12.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/color v1.16.0 // indirect github.com/flatcar/ignition v0.36.2 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-jose/go-jose/v3 v3.0.1 // indirect + github.com/go-jose/go-jose/v4 v4.0.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/analysis v0.22.0 // indirect + github.com/go-openapi/analysis v0.23.0 // indirect github.com/go-openapi/errors v0.22.0 // indirect - github.com/go-openapi/jsonpointer v0.20.2 // indirect - github.com/go-openapi/jsonreference v0.20.4 // indirect - github.com/go-openapi/loads v0.21.5 // indirect - github.com/go-openapi/runtime v0.27.1 // indirect - github.com/go-openapi/spec v0.20.14 // indirect - github.com/go-openapi/swag v0.22.9 // indirect - github.com/go-openapi/validate v0.22.6 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/loads v0.22.0 // indirect + github.com/go-openapi/runtime v0.28.0 // indirect + github.com/go-openapi/spec v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/validate v0.24.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/goccy/go-json v0.10.2 // indirect @@ -62,7 +62,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect @@ -74,9 +74,9 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/httprc v1.0.4 // indirect + github.com/lestrrat-go/httprc v1.0.5 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect - github.com/lestrrat-go/jwx/v2 v2.0.19 // indirect + github.com/lestrrat-go/jwx/v2 v2.0.21 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -84,7 +84,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/metal-stack/security v0.7.2 // indirect + github.com/metal-stack/security v0.8.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -113,14 +113,14 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/vincent-petithory/dataurl v1.0.0 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect - go.opentelemetry.io/otel v1.19.0 // indirect - go.opentelemetry.io/otel/metric v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect go4.org v0.0.0-20201209231011-d4a079459e60 // indirect golang.org/x/crypto v0.21.0 // indirect - golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect + golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 // indirect golang.org/x/net v0.22.0 // indirect golang.org/x/oauth2 v0.18.0 // indirect golang.org/x/sync v0.6.0 // indirect diff --git a/go.sum b/go.sum index 521aa8f..665038c 100644 --- a/go.sum +++ b/go.sum @@ -37,8 +37,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo= -github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4= +github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU= +github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac= github.com/coreos/go-semver v0.1.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= @@ -54,8 +54,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= -github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= +github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= @@ -75,8 +75,8 @@ github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyT github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= -github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= +github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= @@ -85,26 +85,26 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= -github.com/go-openapi/analysis v0.22.0 h1:wQ/d07nf78HNj4u+KiSY0sT234IAyePPbMgpUjUJQR0= -github.com/go-openapi/analysis v0.22.0/go.mod h1:acDnkkCI2QxIo8sSIPgmp1wUlRohV7vfGtAIVae73b0= +github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= +github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= -github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= -github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= -github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= -github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= -github.com/go-openapi/loads v0.21.5 h1:jDzF4dSoHw6ZFADCGltDb2lE4F6De7aWSpe+IcsRzT0= -github.com/go-openapi/loads v0.21.5/go.mod h1:PxTsnFBoBe+z89riT+wYt3prmSBP6GDAQh2l9H1Flz8= -github.com/go-openapi/runtime v0.27.1 h1:ae53yaOoh+fx/X5Eaq8cRmavHgDma65XPZuvBqvJYto= -github.com/go-openapi/runtime v0.27.1/go.mod h1:fijeJEiEclyS8BRurYE1DE5TLb9/KZl6eAdbzjsrlLU= -github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/Do= -github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= +github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= +github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= +github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= -github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= -github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= -github.com/go-openapi/validate v0.22.6 h1:+NhuwcEYpWdO5Nm4bmvhGLW0rt1Fcc532Mu3wpypXfo= -github.com/go-openapi/validate v0.22.6/go.mod h1:eaddXSqKeTg5XpSmj1dYyFTK/95n/XHwcOY+BMxKMyM= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= +github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -140,8 +140,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= @@ -152,7 +152,6 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -205,12 +204,12 @@ github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= -github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk= +github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx/v2 v2.0.19 h1:ekv1qEZE6BVct89QA+pRF6+4pCpfVrOnEJnTnT4RXoY= -github.com/lestrrat-go/jwx/v2 v2.0.19/go.mod h1:l3im3coce1lL2cDeAjqmaR+Awx+X8Ih+2k8BuHNJ4CU= +github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0= +github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -229,10 +228,10 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/metal-stack/metal-go v0.28.1 h1:fn/mMmcXLMGOkVLNec4dFP/oserzkeYr/00zDcumTmU= github.com/metal-stack/metal-go v0.28.1/go.mod h1:Iw4xnzbtcn3qz7YaK0ekCAcLZUyz5E7e0ZCvJ5pX0gU= -github.com/metal-stack/metal-lib v0.15.1 h1:QCmtZ6ci6pHsf3RQnSDbbvYshpyRaxCSeXghVvbDFuA= -github.com/metal-stack/metal-lib v0.15.1/go.mod h1:x1nyPRi+b/WeK7N41cm4R8w4pScnhOYv8hos2UM4lXY= -github.com/metal-stack/security v0.7.2 h1:kUdWej+a0+YPBGt4fT56Mu8cWX/tOjeKL/FWNlUuoe8= -github.com/metal-stack/security v0.7.2/go.mod h1:dTidiZIEzZajwqizrOCTJbmjQSYVbe1tG52IoMlnKZo= +github.com/metal-stack/metal-lib v0.16.0 h1:Zbxq1Qf2H5OKvmSowlVZ5d+sQqIBtiyACeKHvvK3vJg= +github.com/metal-stack/metal-lib v0.16.0/go.mod h1:nyNGI4DZFOcWbSoq2Y6V3SHpFxuXBIqYBZHTb6cy//s= +github.com/metal-stack/security v0.8.0 h1:tVaSDB9m5clwYrnLyaXfPy7mQlJTnmeoHscG+RUy/xo= +github.com/metal-stack/security v0.8.0/go.mod h1:7GAcQb+pOgflW30ohJygxpqc3i0dQ2ahGJK1CU5tqa0= github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs= github.com/metal-stack/v v1.0.3/go.mod h1:YTahEu7/ishwpYKnp/VaW/7nf8+PInogkfGwLcGPdXg= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -248,10 +247,10 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM= -github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= -github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= -github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= +github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= +github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= +github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= @@ -334,14 +333,14 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE= -go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -354,7 +353,6 @@ go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZM golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -368,8 +366,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= -golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc= +golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -596,8 +594,8 @@ k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt 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/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.14.5 h1:6xaWFqzT5KuAQ9ufgUaj1G/+C4Y1GRkhrxl+BJ9i+5s= -sigs.k8s.io/controller-runtime v0.14.5/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +sigs.k8s.io/controller-runtime v0.14.7 h1:Vrnm2vk9ZFlRkXATHz0W0wXcqNl7kPat8q2JyxVy0Q8= +sigs.k8s.io/controller-runtime v0.14.7/go.mod h1:ErTs3SJCOujNUnTz4AS+uh8hp6DHMo1gj6fFndJT1X8= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= From a77469e1b15ad1c957705c31b7e5da8952ea59a0 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 26 Mar 2024 11:22:41 +0100 Subject: [PATCH 05/20] Pin. --- go.mod | 9 +++++---- go.sum | 13 ++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 196c0dd..961e32a 100644 --- a/go.mod +++ b/go.mod @@ -15,14 +15,15 @@ require ( github.com/onsi/ginkgo/v2 v2.17.1 github.com/onsi/gomega v1.32.0 github.com/stretchr/testify v1.9.0 - k8s.io/api v0.28.3 - k8s.io/apimachinery v0.28.3 - k8s.io/client-go v0.28.3 + k8s.io/api v0.29.0 + k8s.io/apimachinery v0.29.0 + k8s.io/client-go v0.29.0 sigs.k8s.io/controller-runtime v0.14.7 ) replace ( k8s.io/api => k8s.io/api v0.26.3 + k8s.io/apimachinery => k8s.io/apimachinery v0.26.3 k8s.io/client-go => k8s.io/client-go v0.26.3 k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f ) @@ -83,7 +84,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/metal-stack/security v0.8.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect diff --git a/go.sum b/go.sum index a218220..76e04c3 100644 --- a/go.sum +++ b/go.sum @@ -77,7 +77,6 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= @@ -147,8 +146,6 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -341,8 +338,10 @@ go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucg go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= @@ -579,8 +578,8 @@ k8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU= k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE= k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0= k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= -k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= -k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis= +k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k= +k8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= k8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s= k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= k8s.io/component-base v0.29.0 h1:T7rjd5wvLnPBV1vC4zWd/iWRbV8Mdxs+nGaoaFzGw3s= From 70601c39023aed13585d708d54eb83bcc29e4903 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 26 Mar 2024 12:49:05 +0100 Subject: [PATCH 06/20] Skip repeated status update. --- api/v2/types_utils.go | 4 ++++ controllers/deployment/reconcile.go | 2 +- controllers/firewall/reconcile.go | 2 +- controllers/generic_controller.go | 34 ++++++++++++++++++++++++----- controllers/monitor/reconcile.go | 2 +- controllers/set/reconcile.go | 2 +- 6 files changed, 36 insertions(+), 10 deletions(-) diff --git a/api/v2/types_utils.go b/api/v2/types_utils.go index a1cd83a..96e5fe7 100644 --- a/api/v2/types_utils.go +++ b/api/v2/types_utils.go @@ -117,6 +117,10 @@ func (cs Conditions) filterOutCondition(t ConditionType) Conditions { func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key string) (bool, error) { annotations := o.GetAnnotations() + if annotations == nil { + return false, nil + } + _, ok := annotations[key] if !ok { return false, nil diff --git a/controllers/deployment/reconcile.go b/controllers/deployment/reconcile.go index fa85ee7..4914653 100644 --- a/controllers/deployment/reconcile.go +++ b/controllers/deployment/reconcile.go @@ -25,7 +25,7 @@ func (c *controller) Reconcile(r *controllers.Ctx[*v2.FirewallDeployment]) error if wasPresent { // the update of the annotation removal triggers the next reconciliation c.log.Info("removed reconcile annotation from resource") - return nil + return controllers.SkipStatusUpdate() } err = c.ensureFirewallControllerRBAC(r) diff --git a/controllers/firewall/reconcile.go b/controllers/firewall/reconcile.go index 1389661..d113188 100644 --- a/controllers/firewall/reconcile.go +++ b/controllers/firewall/reconcile.go @@ -26,7 +26,7 @@ func (c *controller) Reconcile(r *controllers.Ctx[*v2.Firewall]) error { if wasPresent { // the update of the annotation removal triggers the next reconciliation c.log.Info("removed reconcile annotation from resource") - return nil + return controllers.SkipStatusUpdate() } var f *models.V1FirewallResponse diff --git a/controllers/generic_controller.go b/controllers/generic_controller.go index 17772fc..74c43a7 100644 --- a/controllers/generic_controller.go +++ b/controllers/generic_controller.go @@ -53,6 +53,16 @@ func RequeueAfter(d time.Duration, reason string) error { return &requeueError{after: d, reason: reason} } +type skipStatusUpdateError struct{} + +func (e *skipStatusUpdateError) Error() string { + return fmt.Sprintf("skipping resource status update") +} + +func SkipStatusUpdate() error { + return &skipStatusUpdateError{} +} + func NewGenericController[O client.Object](l logr.Logger, c client.Client, namespace string, reconciler Reconciler[O]) *GenericController[O] { return &GenericController[O]{ l: l, @@ -129,10 +139,17 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( } } - var statusErr error + var ( + skipStatusUpdate = false + statusErr error + ) if g.hasStatus { defer func() { + if skipStatusUpdate { + return + } + log.Info("updating status") refetched := g.reconciler.New() @@ -148,6 +165,7 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( if statusErr != nil { log.Error(statusErr, "status could not be updated") } + return }() } @@ -156,13 +174,17 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( err := g.reconciler.Reconcile(rctx) if err != nil { var requeueErr *requeueError - if errors.As(err, &requeueErr) { - log.Info(requeueErr.Error()) + + switch { + case errors.As(err, &requeueErr): + log.Error(requeueErr, "requeueing reconciliation due to an error") return ctrl.Result{RequeueAfter: requeueErr.after}, nil //nolint:nilerr we need to return nil such that the requeue works + case errors.Is(err, &skipStatusUpdateError{}): + return ctrl.Result{}, nil + default: + log.Error(err, "error during reconcile") + return ctrl.Result{}, err } - - log.Error(err, "error during reconcile") - return ctrl.Result{}, err } return ctrl.Result{}, statusErr diff --git a/controllers/monitor/reconcile.go b/controllers/monitor/reconcile.go index 926fa6e..60340b6 100644 --- a/controllers/monitor/reconcile.go +++ b/controllers/monitor/reconcile.go @@ -24,7 +24,7 @@ func (c *controller) Reconcile(r *controllers.Ctx[*v2.FirewallMonitor]) error { if wasPresent { // the update of the annotation removal triggers the next reconciliation c.log.Info("removed reconcile annotation from resource") - return nil + return controllers.SkipStatusUpdate() } _, err = c.updateFirewallStatus(r) diff --git a/controllers/set/reconcile.go b/controllers/set/reconcile.go index 459c5bb..9e49fc4 100644 --- a/controllers/set/reconcile.go +++ b/controllers/set/reconcile.go @@ -20,7 +20,7 @@ func (c *controller) Reconcile(r *controllers.Ctx[*v2.FirewallSet]) error { if wasPresent { // the update of the annotation removal triggers the next reconciliation c.log.Info("removed reconcile annotation from resource") - return nil + return controllers.SkipStatusUpdate() } ownedFirewalls, orphaned, err := controllers.GetOwnedResources(r.Ctx, c.c.GetSeedClient(), r.Target.Spec.Selector, r.Target, &v2.FirewallList{}, func(fl *v2.FirewallList) []*v2.Firewall { From d1fe4f48b8aa43e5e2d8c9b4bff0986812e23a67 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 26 Mar 2024 12:57:49 +0100 Subject: [PATCH 07/20] Skip reconcile for firewall set when owned firewall gets reconcile annotation. --- controllers/generic_controller.go | 2 +- controllers/set/controller.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/controllers/generic_controller.go b/controllers/generic_controller.go index 74c43a7..ddc895f 100644 --- a/controllers/generic_controller.go +++ b/controllers/generic_controller.go @@ -177,7 +177,7 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( switch { case errors.As(err, &requeueErr): - log.Error(requeueErr, "requeueing reconciliation due to an error") + log.Info(requeueErr.Error()) return ctrl.Result{RequeueAfter: requeueErr.after}, nil //nolint:nilerr we need to return nil such that the requeue works case errors.Is(err, &skipStatusUpdateError{}): return ctrl.Result{}, nil diff --git a/controllers/set/controller.go b/controllers/set/controller.go index 6fc09fc..8efe869 100644 --- a/controllers/set/controller.go +++ b/controllers/set/controller.go @@ -37,7 +37,9 @@ func SetupWithManager(log logr.Logger, recorder record.EventRecorder, mgr ctrl.M ), ). Named("FirewallSet"). - Owns(&v2.Firewall{}). + Owns(&v2.Firewall{}, builder.WithPredicates( + v2.SkipAnnotationRemoval(v2.ReconcileAnnotation), + )). WithEventFilter(predicate.NewPredicateFuncs(controllers.SkipOtherNamespace(c.GetSeedNamespace()))). Complete(g) } From 0e483450ed5bab9e5862d635b999a3a71d0d1d36 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 26 Mar 2024 13:07:54 +0100 Subject: [PATCH 08/20] Fix. --- api/v2/types_utils.go | 30 ++++++++++++++++++++++++++++ controllers/deployment/controller.go | 7 ++++++- controllers/set/controller.go | 9 ++++++--- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/api/v2/types_utils.go b/api/v2/types_utils.go index 96e5fe7..fd576cb 100644 --- a/api/v2/types_utils.go +++ b/api/v2/types_utils.go @@ -168,6 +168,36 @@ func annotationWasRemoved(update event.UpdateEvent, annotation string) bool { return o && !n } +// SkipAnnotationAdded returns a predicate when the given annotation key was added. +func SkipAnnotationAdded(annotation string) predicate.Funcs { + return predicate.Funcs{ + UpdateFunc: func(update event.UpdateEvent) bool { + return !annotationWasAdded(update, annotation) + }, + } +} + +func annotationWasAdded(update event.UpdateEvent, annotation string) bool { + if cmp.Equal(update.ObjectOld.GetAnnotations(), update.ObjectNew.GetAnnotations()) { + return false + } + + var ( + _, o = update.ObjectOld.GetAnnotations()[annotation] + _, n = update.ObjectNew.GetAnnotations()[annotation] + ) + + if o { + return false + } + + if !n { + return false + } + + return !o && n +} + // IsAnnotationTrue returns true if the given object has an annotation with a given // key and the value of this annotation is a true boolean. func IsAnnotationTrue(o client.Object, key string) bool { diff --git a/controllers/deployment/controller.go b/controllers/deployment/controller.go index 0af5a13..6563b2c 100644 --- a/controllers/deployment/controller.go +++ b/controllers/deployment/controller.go @@ -43,7 +43,12 @@ func SetupWithManager(log logr.Logger, recorder record.EventRecorder, mgr ctrl.M ), ). Named("FirewallDeployment"). - Owns(&v2.FirewallSet{}). + Owns( + &v2.FirewallSet{}, + builder.WithPredicates( + v2.SkipAnnotationAdded(v2.ReconcileAnnotation), + ), + ). WithEventFilter(predicate.NewPredicateFuncs(controllers.SkipOtherNamespace(c.GetSeedNamespace()))). Complete(g) } diff --git a/controllers/set/controller.go b/controllers/set/controller.go index 8efe869..73a5a1b 100644 --- a/controllers/set/controller.go +++ b/controllers/set/controller.go @@ -37,9 +37,12 @@ func SetupWithManager(log logr.Logger, recorder record.EventRecorder, mgr ctrl.M ), ). Named("FirewallSet"). - Owns(&v2.Firewall{}, builder.WithPredicates( - v2.SkipAnnotationRemoval(v2.ReconcileAnnotation), - )). + Owns( + &v2.Firewall{}, + builder.WithPredicates( + v2.SkipAnnotationAdded(v2.ReconcileAnnotation), + ), + ). WithEventFilter(predicate.NewPredicateFuncs(controllers.SkipOtherNamespace(c.GetSeedNamespace()))). Complete(g) } From 4fe2d1f7de8e4701c2b5406566c944cc5fe1b5f1 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 26 Mar 2024 13:12:43 +0100 Subject: [PATCH 09/20] Forgot skip. --- controllers/generic_controller.go | 1 + 1 file changed, 1 insertion(+) diff --git a/controllers/generic_controller.go b/controllers/generic_controller.go index ddc895f..3ee8457 100644 --- a/controllers/generic_controller.go +++ b/controllers/generic_controller.go @@ -180,6 +180,7 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( log.Info(requeueErr.Error()) return ctrl.Result{RequeueAfter: requeueErr.after}, nil //nolint:nilerr we need to return nil such that the requeue works case errors.Is(err, &skipStatusUpdateError{}): + skipStatusUpdate = true return ctrl.Result{}, nil default: log.Error(err, "error during reconcile") From d5761f67fd6215a9d0ccb902dd84a68e65999eca Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 26 Mar 2024 13:17:55 +0100 Subject: [PATCH 10/20] Better predicates. --- controllers/deployment/controller.go | 5 ++++- controllers/set/controller.go | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/controllers/deployment/controller.go b/controllers/deployment/controller.go index 6563b2c..d099cdc 100644 --- a/controllers/deployment/controller.go +++ b/controllers/deployment/controller.go @@ -46,7 +46,10 @@ func SetupWithManager(log logr.Logger, recorder record.EventRecorder, mgr ctrl.M Owns( &v2.FirewallSet{}, builder.WithPredicates( - v2.SkipAnnotationAdded(v2.ReconcileAnnotation), + predicate.Or( + v2.SkipAnnotationAdded(v2.ReconcileAnnotation), + v2.SkipAnnotationRemoval(v2.ReconcileAnnotation), + ), ), ). WithEventFilter(predicate.NewPredicateFuncs(controllers.SkipOtherNamespace(c.GetSeedNamespace()))). diff --git a/controllers/set/controller.go b/controllers/set/controller.go index 73a5a1b..6259904 100644 --- a/controllers/set/controller.go +++ b/controllers/set/controller.go @@ -40,7 +40,10 @@ func SetupWithManager(log logr.Logger, recorder record.EventRecorder, mgr ctrl.M Owns( &v2.Firewall{}, builder.WithPredicates( - v2.SkipAnnotationAdded(v2.ReconcileAnnotation), + predicate.Or( + v2.SkipAnnotationAdded(v2.ReconcileAnnotation), + v2.SkipAnnotationRemoval(v2.ReconcileAnnotation), + ), ), ). WithEventFilter(predicate.NewPredicateFuncs(controllers.SkipOtherNamespace(c.GetSeedNamespace()))). From 363cf3949f249a76e1a1dfed78fbd7b2773ad628 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 26 Mar 2024 13:23:51 +0100 Subject: [PATCH 11/20] Refetch set before update to stabilize integration test. --- controllers/deployment/reconcile.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/controllers/deployment/reconcile.go b/controllers/deployment/reconcile.go index 4914653..3a87c17 100644 --- a/controllers/deployment/reconcile.go +++ b/controllers/deployment/reconcile.go @@ -168,20 +168,26 @@ func (c *controller) createFirewallSet(r *controllers.Ctx[*v2.FirewallDeployment } func (c *controller) syncFirewallSet(r *controllers.Ctx[*v2.FirewallDeployment], set *v2.FirewallSet) error { - set.Spec.Replicas = r.Target.Spec.Replicas - set.Spec.Template = r.Target.Spec.Template + refetched := &v2.FirewallSet{} + err := c.c.GetSeedClient().Get(r.Ctx, client.ObjectKeyFromObject(set), refetched) + if err != nil { + return fmt.Errorf("unable to update/sync firewall set: %w", err) + } + + refetched.Spec.Replicas = r.Target.Spec.Replicas + refetched.Spec.Template = r.Target.Spec.Template - err := c.c.GetSeedClient().Update(r.Ctx, set) + err = c.c.GetSeedClient().Update(r.Ctx, refetched) if err != nil { return fmt.Errorf("unable to update/sync firewall set: %w", err) } - r.Log.Info("updated firewall set", "set-name", set.Name) + r.Log.Info("updated firewall set", "set-name", refetched.Name) - cond := v2.NewCondition(v2.FirewallDeplomentProgressing, v2.ConditionTrue, "FirewallSetUpdated", fmt.Sprintf("Updated firewall set %q.", set.Name)) + cond := v2.NewCondition(v2.FirewallDeplomentProgressing, v2.ConditionTrue, "FirewallSetUpdated", fmt.Sprintf("Updated firewall set %q.", refetched.Name)) r.Target.Status.Conditions.Set(cond) - c.recorder.Eventf(set, "Normal", "Update", "updated firewallset %s", set.Name) + c.recorder.Eventf(refetched, "Normal", "Update", "updated firewallset %s", refetched.Name) return nil } From 4764f95c2dd912592c035bc729d11dfe10d68b4b Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 26 Mar 2024 14:10:32 +0100 Subject: [PATCH 12/20] Fix predicates. --- api/v2/types_utils.go | 12 +++--- api/v2/types_utils_test.go | 58 ++++++++++++++++++++++++++++ controllers/deployment/controller.go | 8 ++-- controllers/monitor/controller.go | 10 ++++- controllers/set/controller.go | 8 ++-- controllers/set/controller_test.go | 2 +- integration/integration_test.go | 14 ++++++- 7 files changed, 96 insertions(+), 16 deletions(-) diff --git a/api/v2/types_utils.go b/api/v2/types_utils.go index fd576cb..23ddaa4 100644 --- a/api/v2/types_utils.go +++ b/api/v2/types_utils.go @@ -138,11 +138,11 @@ func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key return true, nil } -// SkipAnnotationRemoval returns a predicate when the given annotation key was cleaned up. -func SkipAnnotationRemoval(annotation string) predicate.Funcs { +// AnnotationRemovedPredicate returns a predicate when the given annotation key was removed. +func AnnotationRemovedPredicate(annotation string) predicate.Funcs { return predicate.Funcs{ UpdateFunc: func(update event.UpdateEvent) bool { - return !annotationWasRemoved(update, annotation) + return annotationWasRemoved(update, annotation) }, } } @@ -168,11 +168,11 @@ func annotationWasRemoved(update event.UpdateEvent, annotation string) bool { return o && !n } -// SkipAnnotationAdded returns a predicate when the given annotation key was added. -func SkipAnnotationAdded(annotation string) predicate.Funcs { +// AnnotationAddedPredicate returns a predicate when the given annotation key was added. +func AnnotationAddedPredicate(annotation string) predicate.Funcs { return predicate.Funcs{ UpdateFunc: func(update event.UpdateEvent) bool { - return !annotationWasAdded(update, annotation) + return annotationWasAdded(update, annotation) }, } } diff --git a/api/v2/types_utils_test.go b/api/v2/types_utils_test.go index 488e3e7..3bb68ef 100644 --- a/api/v2/types_utils_test.go +++ b/api/v2/types_utils_test.go @@ -133,3 +133,61 @@ func Test_annotationWasRemoved(t *testing.T) { }) } } + +func Test_annotationWasAdded(t *testing.T) { + tests := []struct { + name string + o map[string]string + n map[string]string + annotation string + want bool + }{ + { + name: "annotation is not present", + annotation: "c", + o: map[string]string{"a": ""}, + n: map[string]string{"b": ""}, + want: false, + }, + { + name: "annotation is present", + annotation: "c", + o: map[string]string{"c": ""}, + n: map[string]string{"c": ""}, + want: false, + }, + { + name: "annotation was added", + annotation: "c", + o: nil, + n: map[string]string{"c": ""}, + want: true, + }, + { + name: "annotation was removed", + annotation: "c", + o: map[string]string{"c": ""}, + n: nil, + want: false, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + if got := annotationWasAdded(event.UpdateEvent{ + ObjectOld: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: tt.o, + }, + }, + ObjectNew: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: tt.n, + }, + }, + }, tt.annotation); got != tt.want { + t.Errorf("annotationWasAdded() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/controllers/deployment/controller.go b/controllers/deployment/controller.go index d099cdc..61e0ba9 100644 --- a/controllers/deployment/controller.go +++ b/controllers/deployment/controller.go @@ -46,9 +46,11 @@ func SetupWithManager(log logr.Logger, recorder record.EventRecorder, mgr ctrl.M Owns( &v2.FirewallSet{}, builder.WithPredicates( - predicate.Or( - v2.SkipAnnotationAdded(v2.ReconcileAnnotation), - v2.SkipAnnotationRemoval(v2.ReconcileAnnotation), + predicate.Not( + predicate.Or( + v2.AnnotationAddedPredicate(v2.ReconcileAnnotation), + v2.AnnotationRemovedPredicate(v2.ReconcileAnnotation), + ), ), ), ). diff --git a/controllers/monitor/controller.go b/controllers/monitor/controller.go index 4b1ccf4..1a4faa7 100644 --- a/controllers/monitor/controller.go +++ b/controllers/monitor/controller.go @@ -3,6 +3,7 @@ package monitor import ( "github.com/go-logr/logr" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/predicate" v2 "github.com/metal-stack/firewall-controller-manager/api/v2" @@ -22,10 +23,15 @@ func SetupWithManager(log logr.Logger, mgr ctrl.Manager, c *config.ControllerCon }).WithoutStatus() return ctrl.NewControllerManagedBy(mgr). - For(&v2.FirewallMonitor{}). + For(&v2.FirewallMonitor{}, + builder.WithPredicates( + predicate.Not( + v2.AnnotationRemovedPredicate(v2.RollSetAnnotation), + ), + ), + ). Named("FirewallMonitor"). WithEventFilter(predicate.NewPredicateFuncs(controllers.SkipOtherNamespace(c.GetShootNamespace()))). - WithEventFilter(v2.SkipAnnotationRemoval(v2.RollSetAnnotation)). Complete(g) } diff --git a/controllers/set/controller.go b/controllers/set/controller.go index 6259904..54873eb 100644 --- a/controllers/set/controller.go +++ b/controllers/set/controller.go @@ -40,9 +40,11 @@ func SetupWithManager(log logr.Logger, recorder record.EventRecorder, mgr ctrl.M Owns( &v2.Firewall{}, builder.WithPredicates( - predicate.Or( - v2.SkipAnnotationAdded(v2.ReconcileAnnotation), - v2.SkipAnnotationRemoval(v2.ReconcileAnnotation), + predicate.Not( + predicate.Or( + v2.AnnotationAddedPredicate(v2.ReconcileAnnotation), + v2.AnnotationRemovedPredicate(v2.ReconcileAnnotation), + ), ), ), ). diff --git a/controllers/set/controller_test.go b/controllers/set/controller_test.go index f142d9a..77ba538 100644 --- a/controllers/set/controller_test.go +++ b/controllers/set/controller_test.go @@ -120,7 +120,7 @@ var _ = Context("firewall set controller", Ordered, func() { firewall := testcommon.WaitForResourceAmount(k8sClient, ctx, namespaceName, 1, &v2.FirewallList{}, func(l *v2.FirewallList) []*v2.Firewall { return l.GetItems() - }, 3*time.Second) + }, 5*time.Second) Expect(firewall.Name).To(Equal(newest.Name), "older firewalls were kept") }) diff --git a/integration/integration_test.go b/integration/integration_test.go index 8117e32..1a6637a 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -830,6 +830,10 @@ var _ = Context("integration test", Ordered, func() { }) Expect(k8sClient.Delete(ctx, deployment())).To(Succeed()) + + _ = testcommon.WaitForResourceAmount(k8sClient, ctx, namespaceName, 0, &v2.FirewallDeploymentList{}, func(l *v2.FirewallDeploymentList) []*v2.FirewallDeployment { + return l.GetItems() + }, 10*time.Second) }) }) @@ -1537,6 +1541,10 @@ var _ = Context("integration test", Ordered, func() { }) Expect(k8sClient.Delete(ctx, deployment())).To(Succeed()) + + _ = testcommon.WaitForResourceAmount(k8sClient, ctx, namespaceName, 0, &v2.FirewallDeploymentList{}, func(l *v2.FirewallDeploymentList) []*v2.FirewallDeployment { + return l.GetItems() + }, 10*time.Second) }) }) @@ -1797,7 +1805,7 @@ var _ = Context("integration test", Ordered, func() { It("should have the progress condition true", func() { cond := testcommon.WaitForCondition(k8sClient, ctx, deployment(), func(fd *v2.FirewallDeployment) v2.Conditions { return fd.Status.Conditions - }, v2.FirewallDeplomentProgressing, v2.ConditionTrue, 15*time.Second) + }, v2.FirewallDeplomentProgressing, v2.ConditionTrue, 30*time.Second) Expect(cond.LastTransitionTime).NotTo(BeZero()) Expect(cond.LastUpdateTime).NotTo(BeZero()) @@ -1841,6 +1849,10 @@ var _ = Context("integration test", Ordered, func() { }) Expect(k8sClient.Delete(ctx, deployment())).To(Succeed()) + + _ = testcommon.WaitForResourceAmount(k8sClient, ctx, namespaceName, 0, &v2.FirewallDeploymentList{}, func(l *v2.FirewallDeploymentList) []*v2.FirewallDeployment { + return l.GetItems() + }, 10*time.Second) }) }) From 46dc8babab3fd689b85831a3bd2523668ab771fe Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 26 Mar 2024 14:25:51 +0100 Subject: [PATCH 13/20] Move into generic controller. --- controllers/deployment/controller.go | 2 +- controllers/deployment/reconcile.go | 45 +++++++++++++--------------- controllers/firewall/controller.go | 2 +- controllers/firewall/reconcile.go | 11 ------- controllers/generic_controller.go | 33 ++++++++------------ controllers/monitor/controller.go | 2 +- controllers/monitor/reconcile.go | 13 +------- controllers/set/controller.go | 2 +- controllers/set/reconcile.go | 11 ------- 9 files changed, 39 insertions(+), 82 deletions(-) diff --git a/controllers/deployment/controller.go b/controllers/deployment/controller.go index 61e0ba9..930a117 100644 --- a/controllers/deployment/controller.go +++ b/controllers/deployment/controller.go @@ -24,7 +24,7 @@ type controller struct { } func SetupWithManager(log logr.Logger, recorder record.EventRecorder, mgr ctrl.Manager, c *config.ControllerConfig) error { - g := controllers.NewGenericController[*v2.FirewallDeployment](log, c.GetSeedClient(), c.GetSeedNamespace(), &controller{ + g := controllers.NewGenericController(log, c.GetSeedClient(), c.GetSeedNamespace(), &controller{ c: c, log: log, recorder: recorder, diff --git a/controllers/deployment/reconcile.go b/controllers/deployment/reconcile.go index 3a87c17..0b19889 100644 --- a/controllers/deployment/reconcile.go +++ b/controllers/deployment/reconcile.go @@ -13,22 +13,12 @@ import ( "github.com/metal-stack/metal-go/api/client/image" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/client-go/util/retry" "sigs.k8s.io/controller-runtime/pkg/client" ) func (c *controller) Reconcile(r *controllers.Ctx[*v2.FirewallDeployment]) error { - wasPresent, err := v2.RemoveAnnotation(r.Ctx, c.c.GetSeedClient(), r.Target, v2.ReconcileAnnotation) - if err != nil { - return err - } - - if wasPresent { - // the update of the annotation removal triggers the next reconciliation - c.log.Info("removed reconcile annotation from resource") - return controllers.SkipStatusUpdate() - } - - err = c.ensureFirewallControllerRBAC(r) + err := c.ensureFirewallControllerRBAC(r) if err != nil { return err } @@ -168,26 +158,33 @@ func (c *controller) createFirewallSet(r *controllers.Ctx[*v2.FirewallDeployment } func (c *controller) syncFirewallSet(r *controllers.Ctx[*v2.FirewallDeployment], set *v2.FirewallSet) error { - refetched := &v2.FirewallSet{} - err := c.c.GetSeedClient().Get(r.Ctx, client.ObjectKeyFromObject(set), refetched) - if err != nil { - return fmt.Errorf("unable to update/sync firewall set: %w", err) - } + err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { + refetched := &v2.FirewallSet{} + err := c.c.GetSeedClient().Get(r.Ctx, client.ObjectKeyFromObject(set), refetched) + if err != nil { + return fmt.Errorf("unable re-fetch firewall set: %w", err) + } - refetched.Spec.Replicas = r.Target.Spec.Replicas - refetched.Spec.Template = r.Target.Spec.Template + refetched.Spec.Replicas = r.Target.Spec.Replicas + refetched.Spec.Template = r.Target.Spec.Template + + err = c.c.GetSeedClient().Update(r.Ctx, refetched) + if err != nil { + return fmt.Errorf("unable to update/sync firewall set: %w", err) + } - err = c.c.GetSeedClient().Update(r.Ctx, refetched) + return nil + }) if err != nil { - return fmt.Errorf("unable to update/sync firewall set: %w", err) + return err } - r.Log.Info("updated firewall set", "set-name", refetched.Name) + r.Log.Info("updated firewall set", "set-name", set.Name) - cond := v2.NewCondition(v2.FirewallDeplomentProgressing, v2.ConditionTrue, "FirewallSetUpdated", fmt.Sprintf("Updated firewall set %q.", refetched.Name)) + cond := v2.NewCondition(v2.FirewallDeplomentProgressing, v2.ConditionTrue, "FirewallSetUpdated", fmt.Sprintf("Updated firewall set %q.", set.Name)) r.Target.Status.Conditions.Set(cond) - c.recorder.Eventf(refetched, "Normal", "Update", "updated firewallset %s", refetched.Name) + c.recorder.Eventf(set, "Normal", "Update", "updated firewallset %s", set.Name) return nil } diff --git a/controllers/firewall/controller.go b/controllers/firewall/controller.go index 65c6eed..d8cec5c 100644 --- a/controllers/firewall/controller.go +++ b/controllers/firewall/controller.go @@ -31,7 +31,7 @@ type controller struct { } func SetupWithManager(log logr.Logger, recorder record.EventRecorder, mgr ctrl.Manager, c *config.ControllerConfig) error { - g := controllers.NewGenericController[*v2.Firewall](log, c.GetSeedClient(), c.GetSeedNamespace(), &controller{ + g := controllers.NewGenericController(log, c.GetSeedClient(), c.GetSeedNamespace(), &controller{ log: log, recorder: recorder, c: c, diff --git a/controllers/firewall/reconcile.go b/controllers/firewall/reconcile.go index d113188..89c07bc 100644 --- a/controllers/firewall/reconcile.go +++ b/controllers/firewall/reconcile.go @@ -18,17 +18,6 @@ import ( // Reconciler must always return either an error or requeue to ensure that it detects if a firewall get lost etc. func (c *controller) Reconcile(r *controllers.Ctx[*v2.Firewall]) error { - wasPresent, err := v2.RemoveAnnotation(r.Ctx, c.c.GetSeedClient(), r.Target, v2.ReconcileAnnotation) - if err != nil { - return err - } - - if wasPresent { - // the update of the annotation removal triggers the next reconciliation - c.log.Info("removed reconcile annotation from resource") - return controllers.SkipStatusUpdate() - } - var f *models.V1FirewallResponse defer func() { if err := c.setStatus(r, f); err != nil { diff --git a/controllers/generic_controller.go b/controllers/generic_controller.go index 3ee8457..1ff1279 100644 --- a/controllers/generic_controller.go +++ b/controllers/generic_controller.go @@ -53,16 +53,6 @@ func RequeueAfter(d time.Duration, reason string) error { return &requeueError{after: d, reason: reason} } -type skipStatusUpdateError struct{} - -func (e *skipStatusUpdateError) Error() string { - return fmt.Sprintf("skipping resource status update") -} - -func SkipStatusUpdate() error { - return &skipStatusUpdateError{} -} - func NewGenericController[O client.Object](l logr.Logger, c client.Client, namespace string, reconciler Reconciler[O]) *GenericController[O] { return &GenericController[O]{ l: l, @@ -139,17 +129,23 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( } } + wasPresent, err := v2.RemoveAnnotation(ctx, g.c, o, v2.ReconcileAnnotation) + if err != nil { + return ctrl.Result{}, fmt.Errorf("unable to remove reconcile annotation: %w", err) + } + + if wasPresent { + // the update of the annotation removal triggers the next reconciliation + log.Info("removed reconcile annotation from resource") + return ctrl.Result{}, nil + } + var ( - skipStatusUpdate = false - statusErr error + statusErr error ) if g.hasStatus { defer func() { - if skipStatusUpdate { - return - } - log.Info("updating status") refetched := g.reconciler.New() @@ -171,7 +167,7 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( } log.Info("reconciling resource") - err := g.reconciler.Reconcile(rctx) + err = g.reconciler.Reconcile(rctx) if err != nil { var requeueErr *requeueError @@ -179,9 +175,6 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( case errors.As(err, &requeueErr): log.Info(requeueErr.Error()) return ctrl.Result{RequeueAfter: requeueErr.after}, nil //nolint:nilerr we need to return nil such that the requeue works - case errors.Is(err, &skipStatusUpdateError{}): - skipStatusUpdate = true - return ctrl.Result{}, nil default: log.Error(err, "error during reconcile") return ctrl.Result{}, err diff --git a/controllers/monitor/controller.go b/controllers/monitor/controller.go index 1a4faa7..cc5d5ee 100644 --- a/controllers/monitor/controller.go +++ b/controllers/monitor/controller.go @@ -17,7 +17,7 @@ type controller struct { } func SetupWithManager(log logr.Logger, mgr ctrl.Manager, c *config.ControllerConfig) error { - g := controllers.NewGenericController[*v2.FirewallMonitor](log, c.GetShootClient(), c.GetShootNamespace(), &controller{ + g := controllers.NewGenericController(log, c.GetShootClient(), c.GetShootNamespace(), &controller{ log: log, c: c, }).WithoutStatus() diff --git a/controllers/monitor/reconcile.go b/controllers/monitor/reconcile.go index 60340b6..5b3797d 100644 --- a/controllers/monitor/reconcile.go +++ b/controllers/monitor/reconcile.go @@ -16,18 +16,7 @@ import ( ) func (c *controller) Reconcile(r *controllers.Ctx[*v2.FirewallMonitor]) error { - wasPresent, err := v2.RemoveAnnotation(r.Ctx, c.c.GetShootClient(), r.Target, v2.ReconcileAnnotation) - if err != nil { - return err - } - - if wasPresent { - // the update of the annotation removal triggers the next reconciliation - c.log.Info("removed reconcile annotation from resource") - return controllers.SkipStatusUpdate() - } - - _, err = c.updateFirewallStatus(r) + _, err := c.updateFirewallStatus(r) if err != nil { r.Log.Error(err, "unable to update firewall status") return controllers.RequeueAfter(3*time.Second, "unable to update firewall status, retrying") diff --git a/controllers/set/controller.go b/controllers/set/controller.go index 54873eb..06909c5 100644 --- a/controllers/set/controller.go +++ b/controllers/set/controller.go @@ -20,7 +20,7 @@ type controller struct { } func SetupWithManager(log logr.Logger, recorder record.EventRecorder, mgr ctrl.Manager, c *config.ControllerConfig) error { - g := controllers.NewGenericController[*v2.FirewallSet](log, c.GetSeedClient(), c.GetSeedNamespace(), &controller{ + g := controllers.NewGenericController(log, c.GetSeedClient(), c.GetSeedNamespace(), &controller{ log: log, recorder: recorder, c: c, diff --git a/controllers/set/reconcile.go b/controllers/set/reconcile.go index 9e49fc4..6591ea3 100644 --- a/controllers/set/reconcile.go +++ b/controllers/set/reconcile.go @@ -12,17 +12,6 @@ import ( ) func (c *controller) Reconcile(r *controllers.Ctx[*v2.FirewallSet]) error { - wasPresent, err := v2.RemoveAnnotation(r.Ctx, c.c.GetSeedClient(), r.Target, v2.ReconcileAnnotation) - if err != nil { - return err - } - - if wasPresent { - // the update of the annotation removal triggers the next reconciliation - c.log.Info("removed reconcile annotation from resource") - return controllers.SkipStatusUpdate() - } - ownedFirewalls, orphaned, err := controllers.GetOwnedResources(r.Ctx, c.c.GetSeedClient(), r.Target.Spec.Selector, r.Target, &v2.FirewallList{}, func(fl *v2.FirewallList) []*v2.Firewall { return fl.GetItems() }) From 48ad743c1524ba6ac6596e5b6b63fd1db3a5e331 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 26 Mar 2024 16:07:14 +0100 Subject: [PATCH 14/20] Also add maintenance annotation. --- api/v2/types_utils.go | 15 ++++++++++++++ controllers/deployment/controller.go | 11 ++++++---- controllers/generic_controller.go | 30 ++++++++++++++++++++++------ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/api/v2/types_utils.go b/api/v2/types_utils.go index 23ddaa4..fc2a7da 100644 --- a/api/v2/types_utils.go +++ b/api/v2/types_utils.go @@ -17,6 +17,9 @@ const ( // ReconcileAnnotation can be used to trigger a reconciliation of a resource managed by a controller. // The value of the annotation does not matter, the controller will cleanup the annotation automatically and trigger a reconciliation of the resource. ReconcileAnnotation = "firewall.metal-stack.io/reconcile" + // MaintenanceAnnotation can be used to trigger a maintenance reconciliation for which a controller might have special behavior. + // The value of the annotation does not matter, the controller will cleanup the annotation automatically. + MaintenanceAnnotation = "firewall.metal-stack.io/maintain" // RollSetAnnotation can be used to trigger a rolling update of a firewall deployment. RollSetAnnotation = "firewall.metal-stack.io/roll-set" // RevisionAnnotation stores the revision number of a resource. @@ -141,9 +144,15 @@ func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key // AnnotationRemovedPredicate returns a predicate when the given annotation key was removed. func AnnotationRemovedPredicate(annotation string) predicate.Funcs { return predicate.Funcs{ + CreateFunc: func(ce event.CreateEvent) bool { + return false + }, UpdateFunc: func(update event.UpdateEvent) bool { return annotationWasRemoved(update, annotation) }, + DeleteFunc: func(de event.DeleteEvent) bool { + return false + }, } } @@ -171,9 +180,15 @@ func annotationWasRemoved(update event.UpdateEvent, annotation string) bool { // AnnotationAddedPredicate returns a predicate when the given annotation key was added. func AnnotationAddedPredicate(annotation string) predicate.Funcs { return predicate.Funcs{ + CreateFunc: func(ce event.CreateEvent) bool { + return false + }, UpdateFunc: func(update event.UpdateEvent) bool { return annotationWasAdded(update, annotation) }, + DeleteFunc: func(de event.DeleteEvent) bool { + return false + }, } } diff --git a/controllers/deployment/controller.go b/controllers/deployment/controller.go index 930a117..a604540 100644 --- a/controllers/deployment/controller.go +++ b/controllers/deployment/controller.go @@ -35,10 +35,13 @@ func SetupWithManager(log logr.Logger, recorder record.EventRecorder, mgr ctrl.M For( &v2.FirewallDeployment{}, builder.WithPredicates( - predicate.Or( - predicate.GenerationChangedPredicate{}, // prevents reconcile on status sub resource update - predicate.AnnotationChangedPredicate{}, - predicate.LabelChangedPredicate{}, + predicate.And( + predicate.Not(v2.AnnotationRemovedPredicate(v2.MaintenanceAnnotation)), + predicate.Or( + predicate.GenerationChangedPredicate{}, // prevents reconcile on status sub resource update + predicate.AnnotationChangedPredicate{}, + predicate.LabelChangedPredicate{}, + ), ), ), ). diff --git a/controllers/generic_controller.go b/controllers/generic_controller.go index 1ff1279..e8bfc90 100644 --- a/controllers/generic_controller.go +++ b/controllers/generic_controller.go @@ -18,9 +18,10 @@ import ( type ( Ctx[O client.Object] struct { - Ctx context.Context - Log logr.Logger - Target O + Ctx context.Context + Log logr.Logger + Target O + InMaintenance bool } Reconciler[O client.Object] interface { // New returns a new object of O. @@ -81,9 +82,10 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( o = g.reconciler.New() log = g.logger(req) rctx = &Ctx[O]{ - Ctx: ctx, - Log: log, - Target: o, + Ctx: ctx, + Log: log, + Target: o, + InMaintenance: false, } ) @@ -166,6 +168,22 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( }() } + if v2.IsAnnotationTrue(o, v2.MaintenanceAnnotation) { + rctx.InMaintenance = true + + defer func() { + refetched := g.reconciler.New() + + _, err := v2.RemoveAnnotation(ctx, g.c, refetched, v2.MaintenanceAnnotation) + if err != nil { + log.Error(err, "unable to cleanup maintenance annotation") + return + } + + log.Info("removed maintenance annotation") + }() + } + log.Info("reconciling resource") err = g.reconciler.Reconcile(rctx) if err != nil { From e227226226bd948d81d82705a82c0e62155ce509 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Fri, 5 Apr 2024 09:03:05 +0200 Subject: [PATCH 15/20] Bit of code cleanup. --- api/v2/types_utils.go | 77 +++++++++++++++++-------------- controllers/generic_controller.go | 19 ++++---- 2 files changed, 51 insertions(+), 45 deletions(-) diff --git a/api/v2/types_utils.go b/api/v2/types_utils.go index fc2a7da..e10d6eb 100644 --- a/api/v2/types_utils.go +++ b/api/v2/types_utils.go @@ -115,6 +115,20 @@ func (cs Conditions) filterOutCondition(t ConditionType) Conditions { return newConditions } +// IsAnnotationTrue returns true if the given object has an annotation with a given +// key and the value of this annotation is a true boolean. +func IsAnnotationTrue(o client.Object, key string) bool { + enabled, err := strconv.ParseBool(o.GetAnnotations()[key]) + return err == nil && enabled +} + +// IsAnnotationFalse returns true if the given object has an annotation with a given +// key and the value of this annotation is a false boolean. +func IsAnnotationFalse(o client.Object, key string) bool { + enabled, err := strconv.ParseBool(o.GetAnnotations()[key]) + return err == nil && !enabled +} + // RemoveAnnotation removes an annotation by a given key from an object if present by updating it with the given client. // It returns true when the annotation was present and removed and an error if the update process went wrong. func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key string) (bool, error) { @@ -141,21 +155,6 @@ func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key return true, nil } -// AnnotationRemovedPredicate returns a predicate when the given annotation key was removed. -func AnnotationRemovedPredicate(annotation string) predicate.Funcs { - return predicate.Funcs{ - CreateFunc: func(ce event.CreateEvent) bool { - return false - }, - UpdateFunc: func(update event.UpdateEvent) bool { - return annotationWasRemoved(update, annotation) - }, - DeleteFunc: func(de event.DeleteEvent) bool { - return false - }, - } -} - func annotationWasRemoved(update event.UpdateEvent, annotation string) bool { if cmp.Equal(update.ObjectOld.GetAnnotations(), update.ObjectNew.GetAnnotations()) { return false @@ -177,21 +176,6 @@ func annotationWasRemoved(update event.UpdateEvent, annotation string) bool { return o && !n } -// AnnotationAddedPredicate returns a predicate when the given annotation key was added. -func AnnotationAddedPredicate(annotation string) predicate.Funcs { - return predicate.Funcs{ - CreateFunc: func(ce event.CreateEvent) bool { - return false - }, - UpdateFunc: func(update event.UpdateEvent) bool { - return annotationWasAdded(update, annotation) - }, - DeleteFunc: func(de event.DeleteEvent) bool { - return false - }, - } -} - func annotationWasAdded(update event.UpdateEvent, annotation string) bool { if cmp.Equal(update.ObjectOld.GetAnnotations(), update.ObjectNew.GetAnnotations()) { return false @@ -213,9 +197,32 @@ func annotationWasAdded(update event.UpdateEvent, annotation string) bool { return !o && n } -// IsAnnotationTrue returns true if the given object has an annotation with a given -// key and the value of this annotation is a true boolean. -func IsAnnotationTrue(o client.Object, key string) bool { - enabled, err := strconv.ParseBool(o.GetAnnotations()[key]) - return err == nil && enabled +// AnnotationAddedPredicate returns a predicate when the given annotation key was added. +func AnnotationAddedPredicate(annotation string) predicate.Funcs { + return predicate.Funcs{ + CreateFunc: func(ce event.CreateEvent) bool { + return false + }, + UpdateFunc: func(update event.UpdateEvent) bool { + return annotationWasAdded(update, annotation) + }, + DeleteFunc: func(de event.DeleteEvent) bool { + return false + }, + } +} + +// AnnotationRemovedPredicate returns a predicate when the given annotation key was removed. +func AnnotationRemovedPredicate(annotation string) predicate.Funcs { + return predicate.Funcs{ + CreateFunc: func(ce event.CreateEvent) bool { + return false + }, + UpdateFunc: func(update event.UpdateEvent) bool { + return annotationWasRemoved(update, annotation) + }, + DeleteFunc: func(de event.DeleteEvent) bool { + return false + }, + } } diff --git a/controllers/generic_controller.go b/controllers/generic_controller.go index e8bfc90..f14df2f 100644 --- a/controllers/generic_controller.go +++ b/controllers/generic_controller.go @@ -131,20 +131,17 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( } } - wasPresent, err := v2.RemoveAnnotation(ctx, g.c, o, v2.ReconcileAnnotation) - if err != nil { - return ctrl.Result{}, fmt.Errorf("unable to remove reconcile annotation: %w", err) - } + if wasPresent, err := v2.RemoveAnnotation(ctx, g.c, o, v2.ReconcileAnnotation); wasPresent { + if err != nil { + return ctrl.Result{}, fmt.Errorf("unable to remove reconcile annotation: %w", err) + } - if wasPresent { // the update of the annotation removal triggers the next reconciliation log.Info("removed reconcile annotation from resource") return ctrl.Result{}, nil } - var ( - statusErr error - ) + var statusErr error if g.hasStatus { defer func() { @@ -169,6 +166,8 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( } if v2.IsAnnotationTrue(o, v2.MaintenanceAnnotation) { + log.Info("reconciling in maintenance mode") + rctx.InMaintenance = true defer func() { @@ -180,12 +179,12 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( return } - log.Info("removed maintenance annotation") + log.Info("cleaned up maintenance annotation") }() } log.Info("reconciling resource") - err = g.reconciler.Reconcile(rctx) + err := g.reconciler.Reconcile(rctx) if err != nil { var requeueErr *requeueError From f0971e32727ca7ea45884a9b2e5527769d9f06a1 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Fri, 5 Apr 2024 09:15:03 +0200 Subject: [PATCH 16/20] More cleanup. --- api/v2/types_utils.go | 19 +++++++++++++------ controllers/generic_controller.go | 24 ++++++++++++++++-------- controllers/monitor/reconcile.go | 13 ++++--------- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/api/v2/types_utils.go b/api/v2/types_utils.go index e10d6eb..ee75b16 100644 --- a/api/v2/types_utils.go +++ b/api/v2/types_utils.go @@ -21,6 +21,7 @@ const ( // The value of the annotation does not matter, the controller will cleanup the annotation automatically. MaintenanceAnnotation = "firewall.metal-stack.io/maintain" // RollSetAnnotation can be used to trigger a rolling update of a firewall deployment. + // The value of the annotation needs to be true otherwise the controller will ignore it. RollSetAnnotation = "firewall.metal-stack.io/roll-set" // RevisionAnnotation stores the revision number of a resource. RevisionAnnotation = "firewall.metal-stack.io/revision" @@ -115,6 +116,13 @@ func (cs Conditions) filterOutCondition(t ConditionType) Conditions { return newConditions } +// IsAnnotationPresent returns true if the given object has an annotation with a given +// key, the value of this annotation does not matter. +func IsAnnotationPresent(o client.Object, key string) bool { + _, ok := o.GetAnnotations()[key] + return ok +} + // IsAnnotationTrue returns true if the given object has an annotation with a given // key and the value of this annotation is a true boolean. func IsAnnotationTrue(o client.Object, key string) bool { @@ -130,17 +138,16 @@ func IsAnnotationFalse(o client.Object, key string) bool { } // RemoveAnnotation removes an annotation by a given key from an object if present by updating it with the given client. -// It returns true when the annotation was present and removed and an error if the update process went wrong. -func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key string) (bool, error) { +func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key string) error { annotations := o.GetAnnotations() if annotations == nil { - return false, nil + return nil } _, ok := annotations[key] if !ok { - return false, nil + return nil } delete(annotations, key) @@ -149,10 +156,10 @@ func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key err := c.Update(ctx, o) if err != nil { - return false, err + return err } - return true, nil + return nil } func annotationWasRemoved(update event.UpdateEvent, annotation string) bool { diff --git a/controllers/generic_controller.go b/controllers/generic_controller.go index f14df2f..604a0a6 100644 --- a/controllers/generic_controller.go +++ b/controllers/generic_controller.go @@ -131,7 +131,8 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( } } - if wasPresent, err := v2.RemoveAnnotation(ctx, g.c, o, v2.ReconcileAnnotation); wasPresent { + if v2.IsAnnotationPresent(o, v2.ReconcileAnnotation) { + err := v2.RemoveAnnotation(ctx, g.c, o, v2.ReconcileAnnotation) if err != nil { return ctrl.Result{}, fmt.Errorf("unable to remove reconcile annotation: %w", err) } @@ -146,17 +147,17 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( if g.hasStatus { defer func() { log.Info("updating status") - refetched := g.reconciler.New() + newO := g.reconciler.New() - statusErr = g.c.Get(ctx, req.NamespacedName, refetched, &client.GetOptions{}) + statusErr = g.c.Get(ctx, req.NamespacedName, newO, &client.GetOptions{}) if statusErr != nil { log.Error(statusErr, "unable to fetch resource before status update") return } - g.reconciler.SetStatus(o, refetched) + g.reconciler.SetStatus(o, newO) - statusErr = g.c.Status().Update(ctx, refetched) + statusErr = g.c.Status().Update(ctx, newO) if statusErr != nil { log.Error(statusErr, "status could not be updated") } @@ -165,15 +166,21 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( }() } - if v2.IsAnnotationTrue(o, v2.MaintenanceAnnotation) { + if v2.IsAnnotationPresent(o, v2.MaintenanceAnnotation) { log.Info("reconciling in maintenance mode") rctx.InMaintenance = true defer func() { - refetched := g.reconciler.New() + newO := g.reconciler.New() - _, err := v2.RemoveAnnotation(ctx, g.c, refetched, v2.MaintenanceAnnotation) + err := g.c.Get(ctx, req.NamespacedName, newO, &client.GetOptions{}) + if err != nil { + log.Error(err, "unable to fetch resource before maintenance annotation removal") + return + } + + err = v2.RemoveAnnotation(ctx, g.c, newO, v2.MaintenanceAnnotation) if err != nil { log.Error(err, "unable to cleanup maintenance annotation") return @@ -184,6 +191,7 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( } log.Info("reconciling resource") + err := g.reconciler.Reconcile(rctx) if err != nil { var requeueErr *requeueError diff --git a/controllers/monitor/reconcile.go b/controllers/monitor/reconcile.go index 5b3797d..23db9f3 100644 --- a/controllers/monitor/reconcile.go +++ b/controllers/monitor/reconcile.go @@ -55,18 +55,13 @@ func (c *controller) updateFirewallStatus(r *controllers.Ctx[*v2.FirewallMonitor func (c *controller) rollSetAnnotation(r *controllers.Ctx[*v2.FirewallMonitor]) error { rollSet := v2.IsAnnotationTrue(r.Target, v2.RollSetAnnotation) - - present, err := v2.RemoveAnnotation(r.Ctx, c.c.GetShootClient(), r.Target, v2.RollSetAnnotation) - if err != nil { - return err - } - - if !present { + if !rollSet { return nil } - if !rollSet { - return nil + err := v2.RemoveAnnotation(r.Ctx, c.c.GetShootClient(), r.Target, v2.RollSetAnnotation) + if err != nil { + return err } r.Log.Info("initiating firewall set roll as requested by user annotation") From b3e6a50dc871166ee8b1a20f440f62a448105540 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Fri, 5 Apr 2024 09:32:07 +0200 Subject: [PATCH 17/20] Cleanup. --- api/v2/types_annotations.go | 183 +++++++++++++++++++++++++++++++ api/v2/types_annotations_test.go | 124 +++++++++++++++++++++ api/v2/types_firewall.go | 14 +-- api/v2/types_firewallset.go | 3 - api/v2/types_utils.go | 136 ----------------------- api/v2/types_utils_test.go | 118 -------------------- controllers/monitor/reconcile.go | 14 +-- 7 files changed, 313 insertions(+), 279 deletions(-) create mode 100644 api/v2/types_annotations.go create mode 100644 api/v2/types_annotations_test.go diff --git a/api/v2/types_annotations.go b/api/v2/types_annotations.go new file mode 100644 index 0000000..344251d --- /dev/null +++ b/api/v2/types_annotations.go @@ -0,0 +1,183 @@ +package v2 + +import ( + "context" + "strconv" + + "github.com/google/go-cmp/cmp" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +const ( + // ReconcileAnnotation can be used to trigger a reconciliation of a resource managed by a controller. + // The value of the annotation does not matter, the controller will cleanup the annotation automatically and trigger a reconciliation of the resource. + ReconcileAnnotation = "firewall.metal-stack.io/reconcile" + // MaintenanceAnnotation can be used to trigger a maintenance reconciliation for which a controller might have special behavior. + // The value of the annotation does not matter, the controller will cleanup the annotation automatically. + MaintenanceAnnotation = "firewall.metal-stack.io/maintain" + // RollSetAnnotation can be used to trigger a rolling update of a firewall deployment. + // The value of the annotation needs to be true otherwise the controller will ignore it. + RollSetAnnotation = "firewall.metal-stack.io/roll-set" + // RevisionAnnotation stores the revision number of a resource. + RevisionAnnotation = "firewall.metal-stack.io/revision" + + // FirewallNoControllerConnectionAnnotation can be used as an annotation to the firewall resource in order + // to indicate that the firewall-controller does not connect to the firewall monitor. this way, the replica + // set will become healthy without a controller connection. + // + // this can be useful to silence a problem temporarily and was used in the past for migration of firewall-controller v1. + FirewallNoControllerConnectionAnnotation = "firewall.metal-stack.io/no-controller-connection" + // FirewallControllerManagedByAnnotation is used as tag for creating a firewall to indicate who is managing the firewall. + FirewallControllerManagedByAnnotation = "firewall.metal-stack.io/managed-by" + // FirewallWeightAnnotation is considered when deciding which firewall is thrown away on scale down. + // Value must be parsable as an integer. Firewalls with higher weight are kept longer. + // Defaults to 0 if no annotation is present. Negative values are allowed. + FirewallWeightAnnotation = "firewall.metal-stack.io/weight" + + // FirewallControllerSetAnnotation is a tag added to the firewall entity indicating to which set a firewall belongs to. + FirewallControllerSetAnnotation = "firewall.metal.stack.io/set" +) + +// IsAnnotationPresent returns true if the given object has an annotation with a given +// key, the value of this annotation does not matter. +func IsAnnotationPresent(o client.Object, key string) bool { + _, ok := o.GetAnnotations()[key] + return ok +} + +// IsAnnotationTrue returns true if the given object has an annotation with a given +// key and the value of this annotation is a true boolean. +func IsAnnotationTrue(o client.Object, key string) bool { + enabled, err := strconv.ParseBool(o.GetAnnotations()[key]) + return err == nil && enabled +} + +// IsAnnotationFalse returns true if the given object has an annotation with a given +// key and the value of this annotation is a false boolean. +func IsAnnotationFalse(o client.Object, key string) bool { + enabled, err := strconv.ParseBool(o.GetAnnotations()[key]) + return err == nil && !enabled +} + +// RemoveAnnotation removes an annotation by a given key from an object if present by updating it with the given client. +func AddAnnotation(ctx context.Context, c client.Client, o client.Object, key, value string) error { + annotations := o.GetAnnotations() + + if annotations == nil { + annotations = map[string]string{} + } + + if existingValue, ok := annotations[key]; ok && existingValue == value { + return nil + } + + annotations[key] = value + + o.SetAnnotations(annotations) + + err := c.Update(ctx, o) + if err != nil { + return err + } + + return nil +} + +// RemoveAnnotation removes an annotation by a given key from an object if present by updating it with the given client. +func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key string) error { + annotations := o.GetAnnotations() + + if annotations == nil { + return nil + } + + _, ok := annotations[key] + if !ok { + return nil + } + + delete(annotations, key) + + o.SetAnnotations(annotations) + + err := c.Update(ctx, o) + if err != nil { + return err + } + + return nil +} + +func annotationWasRemoved(update event.UpdateEvent, annotation string) bool { + if cmp.Equal(update.ObjectOld.GetAnnotations(), update.ObjectNew.GetAnnotations()) { + return false + } + + var ( + _, o = update.ObjectOld.GetAnnotations()[annotation] + _, n = update.ObjectNew.GetAnnotations()[annotation] + ) + + if n { + return false + } + + if !o { + return false + } + + return o && !n +} + +func annotationWasAdded(update event.UpdateEvent, annotation string) bool { + if cmp.Equal(update.ObjectOld.GetAnnotations(), update.ObjectNew.GetAnnotations()) { + return false + } + + var ( + _, o = update.ObjectOld.GetAnnotations()[annotation] + _, n = update.ObjectNew.GetAnnotations()[annotation] + ) + + if o { + return false + } + + if !n { + return false + } + + return !o && n +} + +// AnnotationAddedPredicate returns a predicate when the given annotation key was added. +func AnnotationAddedPredicate(annotation string) predicate.Funcs { + return predicate.Funcs{ + CreateFunc: func(ce event.CreateEvent) bool { + return false + }, + UpdateFunc: func(update event.UpdateEvent) bool { + return annotationWasAdded(update, annotation) + }, + DeleteFunc: func(de event.DeleteEvent) bool { + return false + }, + } +} + +// AnnotationRemovedPredicate returns a predicate when the given annotation key was removed. +func AnnotationRemovedPredicate(annotation string) predicate.Funcs { + return predicate.Funcs{ + CreateFunc: func(ce event.CreateEvent) bool { + return false + }, + UpdateFunc: func(update event.UpdateEvent) bool { + return annotationWasRemoved(update, annotation) + }, + DeleteFunc: func(de event.DeleteEvent) bool { + return false + }, + } +} diff --git a/api/v2/types_annotations_test.go b/api/v2/types_annotations_test.go new file mode 100644 index 0000000..9e5aefb --- /dev/null +++ b/api/v2/types_annotations_test.go @@ -0,0 +1,124 @@ +package v2 + +import ( + "testing" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/event" +) + +func Test_annotationWasRemoved(t *testing.T) { + tests := []struct { + name string + o map[string]string + n map[string]string + annotation string + want bool + }{ + { + name: "annotation is not present", + annotation: "c", + o: map[string]string{"a": ""}, + n: map[string]string{"b": ""}, + want: false, + }, + { + name: "annotation is present", + annotation: "c", + o: map[string]string{"c": ""}, + n: map[string]string{"c": ""}, + want: false, + }, + { + name: "annotation was added", + annotation: "c", + o: nil, + n: map[string]string{"c": ""}, + want: false, + }, + { + name: "annotation was removed", + annotation: "c", + o: map[string]string{"c": ""}, + n: nil, + want: true, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + if got := annotationWasRemoved(event.UpdateEvent{ + ObjectOld: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: tt.o, + }, + }, + ObjectNew: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: tt.n, + }, + }, + }, tt.annotation); got != tt.want { + t.Errorf("annotationWasRemoved() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_annotationWasAdded(t *testing.T) { + tests := []struct { + name string + o map[string]string + n map[string]string + annotation string + want bool + }{ + { + name: "annotation is not present", + annotation: "c", + o: map[string]string{"a": ""}, + n: map[string]string{"b": ""}, + want: false, + }, + { + name: "annotation is present", + annotation: "c", + o: map[string]string{"c": ""}, + n: map[string]string{"c": ""}, + want: false, + }, + { + name: "annotation was added", + annotation: "c", + o: nil, + n: map[string]string{"c": ""}, + want: true, + }, + { + name: "annotation was removed", + annotation: "c", + o: map[string]string{"c": ""}, + n: nil, + want: false, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + if got := annotationWasAdded(event.UpdateEvent{ + ObjectOld: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: tt.o, + }, + }, + ObjectNew: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: tt.n, + }, + }, + }, tt.annotation); got != tt.want { + t.Errorf("annotationWasAdded() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/api/v2/types_firewall.go b/api/v2/types_firewall.go index 322ce25..e768905 100644 --- a/api/v2/types_firewall.go +++ b/api/v2/types_firewall.go @@ -9,18 +9,6 @@ import ( ) const ( - // FirewallNoControllerConnectionAnnotation can be used as an annotation to the firewall resource in order - // to indicate that the firewall-controller does not connect to the firewall monitor. this way, the replica - // set will become healthy without a controller connection. - // - // this can be useful to silence a problem temporarily and was used in the past for migration of firewall-controller v1. - FirewallNoControllerConnectionAnnotation = "firewall.metal-stack.io/no-controller-connection" - // FirewallControllerManagedByAnnotation is used as tag for creating a firewall to indicate who is managing the firewall. - FirewallControllerManagedByAnnotation = "firewall.metal-stack.io/managed-by" - // FirewallWeightAnnotation is considered when deciding which firewall is thrown away on scale down. - // Value must be parsable as an integer. Firewalls with higher weight are kept longer. - // Defaults to 0 if no annotation is present. Negative values are allowed. - FirewallWeightAnnotation = "firewall.metal-stack.io/weight" // FirewallControllerManager is a name of the firewall-controller-manager managing the firewall. FirewallControllerManager = "firewall-controller-manager" ) @@ -327,7 +315,7 @@ func SortFirewallsByImportance(fws []*Firewall) { a := fws[i] b := fws[j] - // prefer heigher weight + // prefer higher weight if weight(a) > weight(b) { return true } diff --git a/api/v2/types_firewallset.go b/api/v2/types_firewallset.go index edc8409..cfd58fa 100644 --- a/api/v2/types_firewallset.go +++ b/api/v2/types_firewallset.go @@ -10,9 +10,6 @@ import ( // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. const ( - // FirewallControllerSetAnnotation is a tag added to the firewall entity indicating to which set a firewall belongs to. - FirewallControllerSetAnnotation = "firewall.metal.stack.io/set" - FirewallShortestDistance = FirewallDistance(0) FirewallRollingUpdateSetDistance = FirewallDistance(3) FirewallLongestDistance = FirewallDistance(8) diff --git a/api/v2/types_utils.go b/api/v2/types_utils.go index ee75b16..644be46 100644 --- a/api/v2/types_utils.go +++ b/api/v2/types_utils.go @@ -1,30 +1,12 @@ package v2 import ( - "context" - "strconv" - - "github.com/google/go-cmp/cmp" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" ) const ( // FinalizerName is the finalizer name used by this controller. FinalizerName = "firewall.metal-stack.io/firewall-controller-manager" - // ReconcileAnnotation can be used to trigger a reconciliation of a resource managed by a controller. - // The value of the annotation does not matter, the controller will cleanup the annotation automatically and trigger a reconciliation of the resource. - ReconcileAnnotation = "firewall.metal-stack.io/reconcile" - // MaintenanceAnnotation can be used to trigger a maintenance reconciliation for which a controller might have special behavior. - // The value of the annotation does not matter, the controller will cleanup the annotation automatically. - MaintenanceAnnotation = "firewall.metal-stack.io/maintain" - // RollSetAnnotation can be used to trigger a rolling update of a firewall deployment. - // The value of the annotation needs to be true otherwise the controller will ignore it. - RollSetAnnotation = "firewall.metal-stack.io/roll-set" - // RevisionAnnotation stores the revision number of a resource. - RevisionAnnotation = "firewall.metal-stack.io/revision" ) // ConditionStatus is the status of a condition. @@ -115,121 +97,3 @@ func (cs Conditions) filterOutCondition(t ConditionType) Conditions { } return newConditions } - -// IsAnnotationPresent returns true if the given object has an annotation with a given -// key, the value of this annotation does not matter. -func IsAnnotationPresent(o client.Object, key string) bool { - _, ok := o.GetAnnotations()[key] - return ok -} - -// IsAnnotationTrue returns true if the given object has an annotation with a given -// key and the value of this annotation is a true boolean. -func IsAnnotationTrue(o client.Object, key string) bool { - enabled, err := strconv.ParseBool(o.GetAnnotations()[key]) - return err == nil && enabled -} - -// IsAnnotationFalse returns true if the given object has an annotation with a given -// key and the value of this annotation is a false boolean. -func IsAnnotationFalse(o client.Object, key string) bool { - enabled, err := strconv.ParseBool(o.GetAnnotations()[key]) - return err == nil && !enabled -} - -// RemoveAnnotation removes an annotation by a given key from an object if present by updating it with the given client. -func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key string) error { - annotations := o.GetAnnotations() - - if annotations == nil { - return nil - } - - _, ok := annotations[key] - if !ok { - return nil - } - - delete(annotations, key) - - o.SetAnnotations(annotations) - - err := c.Update(ctx, o) - if err != nil { - return err - } - - return nil -} - -func annotationWasRemoved(update event.UpdateEvent, annotation string) bool { - if cmp.Equal(update.ObjectOld.GetAnnotations(), update.ObjectNew.GetAnnotations()) { - return false - } - - var ( - _, o = update.ObjectOld.GetAnnotations()[annotation] - _, n = update.ObjectNew.GetAnnotations()[annotation] - ) - - if n { - return false - } - - if !o { - return false - } - - return o && !n -} - -func annotationWasAdded(update event.UpdateEvent, annotation string) bool { - if cmp.Equal(update.ObjectOld.GetAnnotations(), update.ObjectNew.GetAnnotations()) { - return false - } - - var ( - _, o = update.ObjectOld.GetAnnotations()[annotation] - _, n = update.ObjectNew.GetAnnotations()[annotation] - ) - - if o { - return false - } - - if !n { - return false - } - - return !o && n -} - -// AnnotationAddedPredicate returns a predicate when the given annotation key was added. -func AnnotationAddedPredicate(annotation string) predicate.Funcs { - return predicate.Funcs{ - CreateFunc: func(ce event.CreateEvent) bool { - return false - }, - UpdateFunc: func(update event.UpdateEvent) bool { - return annotationWasAdded(update, annotation) - }, - DeleteFunc: func(de event.DeleteEvent) bool { - return false - }, - } -} - -// AnnotationRemovedPredicate returns a predicate when the given annotation key was removed. -func AnnotationRemovedPredicate(annotation string) predicate.Funcs { - return predicate.Funcs{ - CreateFunc: func(ce event.CreateEvent) bool { - return false - }, - UpdateFunc: func(update event.UpdateEvent) bool { - return annotationWasRemoved(update, annotation) - }, - DeleteFunc: func(de event.DeleteEvent) bool { - return false - }, - } -} diff --git a/api/v2/types_utils_test.go b/api/v2/types_utils_test.go index 3bb68ef..5ecad28 100644 --- a/api/v2/types_utils_test.go +++ b/api/v2/types_utils_test.go @@ -5,8 +5,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/event" ) func TestConditions(t *testing.T) { @@ -75,119 +73,3 @@ func TestConditions(t *testing.T) { t.Errorf("diff (+got -want):\n %s", diff) } } - -func Test_annotationWasRemoved(t *testing.T) { - tests := []struct { - name string - o map[string]string - n map[string]string - annotation string - want bool - }{ - { - name: "annotation is not present", - annotation: "c", - o: map[string]string{"a": ""}, - n: map[string]string{"b": ""}, - want: false, - }, - { - name: "annotation is present", - annotation: "c", - o: map[string]string{"c": ""}, - n: map[string]string{"c": ""}, - want: false, - }, - { - name: "annotation was added", - annotation: "c", - o: nil, - n: map[string]string{"c": ""}, - want: false, - }, - { - name: "annotation was removed", - annotation: "c", - o: map[string]string{"c": ""}, - n: nil, - want: true, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - if got := annotationWasRemoved(event.UpdateEvent{ - ObjectOld: &Firewall{ - ObjectMeta: v1.ObjectMeta{ - Annotations: tt.o, - }, - }, - ObjectNew: &Firewall{ - ObjectMeta: v1.ObjectMeta{ - Annotations: tt.n, - }, - }, - }, tt.annotation); got != tt.want { - t.Errorf("annotationWasRemoved() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_annotationWasAdded(t *testing.T) { - tests := []struct { - name string - o map[string]string - n map[string]string - annotation string - want bool - }{ - { - name: "annotation is not present", - annotation: "c", - o: map[string]string{"a": ""}, - n: map[string]string{"b": ""}, - want: false, - }, - { - name: "annotation is present", - annotation: "c", - o: map[string]string{"c": ""}, - n: map[string]string{"c": ""}, - want: false, - }, - { - name: "annotation was added", - annotation: "c", - o: nil, - n: map[string]string{"c": ""}, - want: true, - }, - { - name: "annotation was removed", - annotation: "c", - o: map[string]string{"c": ""}, - n: nil, - want: false, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - if got := annotationWasAdded(event.UpdateEvent{ - ObjectOld: &Firewall{ - ObjectMeta: v1.ObjectMeta{ - Annotations: tt.o, - }, - }, - ObjectNew: &Firewall{ - ObjectMeta: v1.ObjectMeta{ - Annotations: tt.n, - }, - }, - }, tt.annotation); got != tt.want { - t.Errorf("annotationWasAdded() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/controllers/monitor/reconcile.go b/controllers/monitor/reconcile.go index 23db9f3..d85e445 100644 --- a/controllers/monitor/reconcile.go +++ b/controllers/monitor/reconcile.go @@ -59,11 +59,6 @@ func (c *controller) rollSetAnnotation(r *controllers.Ctx[*v2.FirewallMonitor]) return nil } - err := v2.RemoveAnnotation(r.Ctx, c.c.GetShootClient(), r.Target, v2.RollSetAnnotation) - if err != nil { - return err - } - r.Log.Info("initiating firewall set roll as requested by user annotation") fw := &v2.Firewall{ @@ -78,14 +73,15 @@ func (c *controller) rollSetAnnotation(r *controllers.Ctx[*v2.FirewallMonitor]) return client.IgnoreNotFound(err) } - set.Annotations[v2.RollSetAnnotation] = strconv.FormatBool(true) - - err = c.c.GetSeedClient().Update(r.Ctx, set) + err = v2.AddAnnotation(r.Ctx, c.c.GetSeedClient(), set, v2.RollSetAnnotation, strconv.FormatBool(true)) if err != nil { return fmt.Errorf("unable to annotate firewall set: %w", err) } - r.Log.Info("firewall set annotated") + err = v2.RemoveAnnotation(r.Ctx, c.c.GetShootClient(), r.Target, v2.RollSetAnnotation) + if err != nil { + return fmt.Errorf("unable to cleanup firewall monitor roll-set annotation: %w", err) + } return nil } From 06941b7cb3676b2af23038ae80dbb7b0af4e1490 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Fri, 5 Apr 2024 14:22:42 +0200 Subject: [PATCH 18/20] More tests. --- api/v2/types_annotations.go | 62 +++---- api/v2/types_annotations_test.go | 303 +++++++++++++++++++++++++++++++ 2 files changed, 334 insertions(+), 31 deletions(-) diff --git a/api/v2/types_annotations.go b/api/v2/types_annotations.go index 344251d..9d10fea 100644 --- a/api/v2/types_annotations.go +++ b/api/v2/types_annotations.go @@ -61,7 +61,7 @@ func IsAnnotationFalse(o client.Object, key string) bool { return err == nil && !enabled } -// RemoveAnnotation removes an annotation by a given key from an object if present by updating it with the given client. +// AddAnnotation adds an annotation by a given key to an object if not present by updating it with the given client. func AddAnnotation(ctx context.Context, c client.Client, o client.Object, key, value string) error { annotations := o.GetAnnotations() @@ -110,6 +110,36 @@ func RemoveAnnotation(ctx context.Context, c client.Client, o client.Object, key return nil } +// AnnotationAddedPredicate returns a predicate when the given annotation key was added. +func AnnotationAddedPredicate(annotation string) predicate.Funcs { + return predicate.Funcs{ + CreateFunc: func(ce event.CreateEvent) bool { + return false + }, + UpdateFunc: func(update event.UpdateEvent) bool { + return annotationWasAdded(update, annotation) + }, + DeleteFunc: func(de event.DeleteEvent) bool { + return false + }, + } +} + +// AnnotationRemovedPredicate returns a predicate when the given annotation key was removed. +func AnnotationRemovedPredicate(annotation string) predicate.Funcs { + return predicate.Funcs{ + CreateFunc: func(ce event.CreateEvent) bool { + return false + }, + UpdateFunc: func(update event.UpdateEvent) bool { + return annotationWasRemoved(update, annotation) + }, + DeleteFunc: func(de event.DeleteEvent) bool { + return false + }, + } +} + func annotationWasRemoved(update event.UpdateEvent, annotation string) bool { if cmp.Equal(update.ObjectOld.GetAnnotations(), update.ObjectNew.GetAnnotations()) { return false @@ -151,33 +181,3 @@ func annotationWasAdded(update event.UpdateEvent, annotation string) bool { return !o && n } - -// AnnotationAddedPredicate returns a predicate when the given annotation key was added. -func AnnotationAddedPredicate(annotation string) predicate.Funcs { - return predicate.Funcs{ - CreateFunc: func(ce event.CreateEvent) bool { - return false - }, - UpdateFunc: func(update event.UpdateEvent) bool { - return annotationWasAdded(update, annotation) - }, - DeleteFunc: func(de event.DeleteEvent) bool { - return false - }, - } -} - -// AnnotationRemovedPredicate returns a predicate when the given annotation key was removed. -func AnnotationRemovedPredicate(annotation string) predicate.Funcs { - return predicate.Funcs{ - CreateFunc: func(ce event.CreateEvent) bool { - return false - }, - UpdateFunc: func(update event.UpdateEvent) bool { - return annotationWasRemoved(update, annotation) - }, - DeleteFunc: func(de event.DeleteEvent) bool { - return false - }, - } -} diff --git a/api/v2/types_annotations_test.go b/api/v2/types_annotations_test.go index 9e5aefb..ce8060c 100644 --- a/api/v2/types_annotations_test.go +++ b/api/v2/types_annotations_test.go @@ -1,12 +1,315 @@ package v2 import ( + "context" "testing" + "github.com/google/go-cmp/cmp" + "github.com/metal-stack/metal-lib/pkg/testcommon" + "github.com/stretchr/testify/require" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/event" ) +func TestIsAnnotationPresent(t *testing.T) { + tests := []struct { + name string + o client.Object + key string + want bool + }{ + { + name: "not present", + o: &Firewall{}, + key: "a", + want: false, + }, + { + name: "present", + o: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + "a": "", + }, + }, + }, + key: "a", + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsAnnotationPresent(tt.o, tt.key); got != tt.want { + t.Errorf("IsAnnotationPresent() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestIsAnnotationIsTrue(t *testing.T) { + tests := []struct { + name string + o client.Object + key string + want bool + }{ + { + name: "not present", + o: &Firewall{}, + key: "a", + want: false, + }, + { + name: "not true", + o: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + "a": "foo", + }, + }, + }, + key: "a", + want: false, + }, + { + name: "true", + o: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + "a": "true", + }, + }, + }, + key: "a", + want: true, + }, + { + name: "different variant of true is also allowed", + o: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + "a": "1", + }, + }, + }, + key: "a", + want: true, + }, + { + name: "false", + o: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + "a": "false", + }, + }, + }, + key: "a", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsAnnotationTrue(tt.o, tt.key); got != tt.want { + t.Errorf("IsAnnotationTrue() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestIsAnnotationIsFalse(t *testing.T) { + tests := []struct { + name string + o client.Object + key string + want bool + }{ + { + name: "not present", + o: &Firewall{}, + key: "a", + want: false, + }, + { + name: "not true", + o: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + "a": "foo", + }, + }, + }, + key: "a", + want: false, + }, + { + name: "true", + o: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + "a": "true", + }, + }, + }, + key: "a", + want: false, + }, + { + name: "false", + o: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + "a": "false", + }, + }, + }, + key: "a", + want: true, + }, + { + name: "different variant of false is also allowed", + o: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Annotations: map[string]string{ + "a": "0", + }, + }, + }, + key: "a", + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsAnnotationFalse(tt.o, tt.key); got != tt.want { + t.Errorf("IsAnnotationFalse() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestAddAnnotation(t *testing.T) { + scheme := runtime.NewScheme() + err := AddToScheme(scheme) + require.NoError(t, err) + ctx := context.Background() + + tests := []struct { + name string + o *Firewall + key string + value string + wantErr error + want *Firewall + }{ + { + name: "add", + o: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Name: "test", + ResourceVersion: "0", + }, + }, + key: "test", + value: "true", + wantErr: nil, + want: &Firewall{ + TypeMeta: v1.TypeMeta{ + Kind: "Firewall", + APIVersion: "firewall.metal-stack.io/v2", + }, + ObjectMeta: v1.ObjectMeta{ + Name: "test", + ResourceVersion: "1", + Annotations: map[string]string{ + "test": "true", + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.o).Build() + + err := AddAnnotation(ctx, c, tt.o, tt.key, tt.value) + if diff := cmp.Diff(tt.wantErr, err, testcommon.ErrorStringComparer()); diff != "" { + t.Errorf("error diff (+got -want):\n %s", diff) + } + + updated := &Firewall{} + err = c.Get(ctx, client.ObjectKeyFromObject(tt.o), updated) + require.NoError(t, err) + + if diff := cmp.Diff(updated, tt.want); diff != "" { + t.Errorf("diff (+got -want):\n %s", diff) + } + }) + } +} + +func TestRemoveAnnotation(t *testing.T) { + scheme := runtime.NewScheme() + err := AddToScheme(scheme) + require.NoError(t, err) + ctx := context.Background() + + tests := []struct { + name string + o *Firewall + key string + value string + wantErr error + want *Firewall + }{ + { + name: "remove", + o: &Firewall{ + ObjectMeta: v1.ObjectMeta{ + Name: "test", + ResourceVersion: "0", + Annotations: map[string]string{ + "test": "true", + }, + }, + }, + key: "test", + wantErr: nil, + want: &Firewall{ + TypeMeta: v1.TypeMeta{ + Kind: "Firewall", + APIVersion: "firewall.metal-stack.io/v2", + }, + ObjectMeta: v1.ObjectMeta{ + Name: "test", + ResourceVersion: "1", + Annotations: nil, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.o).Build() + + err := RemoveAnnotation(ctx, c, tt.o, tt.key) + if diff := cmp.Diff(tt.wantErr, err, testcommon.ErrorStringComparer()); diff != "" { + t.Errorf("error diff (+got -want):\n %s", diff) + } + + updated := &Firewall{} + err = c.Get(ctx, client.ObjectKeyFromObject(tt.o), updated) + require.NoError(t, err) + + if diff := cmp.Diff(updated, tt.want); diff != "" { + t.Errorf("diff (+got -want):\n %s", diff) + } + }) + } +} func Test_annotationWasRemoved(t *testing.T) { tests := []struct { name string From 40f985def75a67613c42ae3896b2a8a7b2c3f93e Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 16 Apr 2024 09:31:12 +0200 Subject: [PATCH 19/20] Just a rename. --- controllers/generic_controller.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/controllers/generic_controller.go b/controllers/generic_controller.go index 604a0a6..0cbbb4e 100644 --- a/controllers/generic_controller.go +++ b/controllers/generic_controller.go @@ -147,17 +147,17 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( if g.hasStatus { defer func() { log.Info("updating status") - newO := g.reconciler.New() + obj := g.reconciler.New() - statusErr = g.c.Get(ctx, req.NamespacedName, newO, &client.GetOptions{}) + statusErr = g.c.Get(ctx, req.NamespacedName, obj, &client.GetOptions{}) if statusErr != nil { log.Error(statusErr, "unable to fetch resource before status update") return } - g.reconciler.SetStatus(o, newO) + g.reconciler.SetStatus(o, obj) - statusErr = g.c.Status().Update(ctx, newO) + statusErr = g.c.Status().Update(ctx, obj) if statusErr != nil { log.Error(statusErr, "status could not be updated") } @@ -172,15 +172,15 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( rctx.InMaintenance = true defer func() { - newO := g.reconciler.New() + obj := g.reconciler.New() - err := g.c.Get(ctx, req.NamespacedName, newO, &client.GetOptions{}) + err := g.c.Get(ctx, req.NamespacedName, obj, &client.GetOptions{}) if err != nil { log.Error(err, "unable to fetch resource before maintenance annotation removal") return } - err = v2.RemoveAnnotation(ctx, g.c, newO, v2.MaintenanceAnnotation) + err = v2.RemoveAnnotation(ctx, g.c, obj, v2.MaintenanceAnnotation) if err != nil { log.Error(err, "unable to cleanup maintenance annotation") return From 8bea0fe62995ee5cf75c20b9f2572d3f45ff9af3 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 23 Apr 2024 09:42:17 +0200 Subject: [PATCH 20/20] Rename. --- controllers/generic_controller.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/controllers/generic_controller.go b/controllers/generic_controller.go index 0cbbb4e..0d60c16 100644 --- a/controllers/generic_controller.go +++ b/controllers/generic_controller.go @@ -18,10 +18,10 @@ import ( type ( Ctx[O client.Object] struct { - Ctx context.Context - Log logr.Logger - Target O - InMaintenance bool + Ctx context.Context + Log logr.Logger + Target O + WithinMaintenance bool } Reconciler[O client.Object] interface { // New returns a new object of O. @@ -82,10 +82,10 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( o = g.reconciler.New() log = g.logger(req) rctx = &Ctx[O]{ - Ctx: ctx, - Log: log, - Target: o, - InMaintenance: false, + Ctx: ctx, + Log: log, + Target: o, + WithinMaintenance: false, } ) @@ -169,7 +169,7 @@ func (g GenericController[O]) Reconcile(ctx context.Context, req ctrl.Request) ( if v2.IsAnnotationPresent(o, v2.MaintenanceAnnotation) { log.Info("reconciling in maintenance mode") - rctx.InMaintenance = true + rctx.WithinMaintenance = true defer func() { obj := g.reconciler.New()