diff --git a/README.md b/README.md index 1c2f538..efcfdcb 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,23 @@ argo-rollout-config-keeper is a Kubernetes controller for managing Argo Rollout application configurations. It ensures that the specified configurations versions are applied and maintained across the cluster. -Its Also adding the following annotation to the outdated version of managed configMaps and secrets: `argocd.argoproj.io/compare-options: IgnoreExtraneous` to ignore them from the application sync global status. More details can be found in [ArgoCd documentation](https://argo-cd.readthedocs.io/en/stable/user-guide/compare-options/) +It's Also adding the following annotation to the outdated version of managed configMaps and secrets: `argocd.argoproj.io/compare-options: IgnoreExtraneous` to ignore them from the application sync global status. More details can be found in [ArgoCd documentation](https://argo-cd.readthedocs.io/en/stable/user-guide/compare-options/) + +There are two main CRDs that the controller is managing: +- ArgoRolloutConfigKeeper. +- ArgoRolloutConfigKeeperClusterScope. + +The ArgoRolloutConfigKeeper is managing the configMaps and secrets in the namespace scope, while the ArgoRolloutConfigKeeperClusterScope is managing the configMaps and secrets in the cluster scope. + +> [!WARNING] +> +> Please note, both CRDs are having the following optional fields to override the operator defaults values, and it's important for the operator mechanism because it filtered the ReplicaSets by them: +> - `appLabel`, if it not set it will get the following default value: `app.kubernetes.io/name`. +> - `appVersionLabel`, if it not set it will get the following default value: `app.kubernetes.io/version`. + +you can see example yamls, [here](../config/samples/): + + ## Getting Started ### Prerequisites diff --git a/api/v1alpha1/argorolloutconfigkeeper_types.go b/api/v1alpha1/argorolloutconfigkeeper_types.go index 3d07771..2664803 100644 --- a/api/v1alpha1/argorolloutconfigkeeper_types.go +++ b/api/v1alpha1/argorolloutconfigkeeper_types.go @@ -51,7 +51,7 @@ func (in *ArgoRolloutConfigKeeperSpec) UnmarshalJSON(b []byte) error { // ArgoRolloutConfigKeeperStatus defines the observed state of ArgoRolloutConfigKeeper type ArgoRolloutConfigKeeperStatus struct { - State string `json:"state"` + Conditions []metav1.Condition `json:"conditions,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/argorolloutconfigkeeperclusterscope_types.go b/api/v1alpha1/argorolloutconfigkeeperclusterscope_types.go index 1486e93..3425f84 100644 --- a/api/v1alpha1/argorolloutconfigkeeperclusterscope_types.go +++ b/api/v1alpha1/argorolloutconfigkeeperclusterscope_types.go @@ -52,7 +52,7 @@ func (in *ArgoRolloutConfigKeeperClusterScopeSpec) UnmarshalJSON(b []byte) error // ArgoRolloutConfigKeeperClusterScopeStatus defines the observed state of ArgoRolloutConfigKeeperClusterScope type ArgoRolloutConfigKeeperClusterScopeStatus struct { - State string `json:"state"` + Conditions []metav1.Condition `json:"conditions,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 46edac8..3898f5c 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package v1alpha1 import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -30,7 +31,7 @@ func (in *ArgoRolloutConfigKeeper) DeepCopyInto(out *ArgoRolloutConfigKeeper) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArgoRolloutConfigKeeper. @@ -57,7 +58,7 @@ func (in *ArgoRolloutConfigKeeperClusterScope) DeepCopyInto(out *ArgoRolloutConf out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArgoRolloutConfigKeeperClusterScope. @@ -140,6 +141,13 @@ func (in *ArgoRolloutConfigKeeperClusterScopeSpec) DeepCopy() *ArgoRolloutConfig // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ArgoRolloutConfigKeeperClusterScopeStatus) DeepCopyInto(out *ArgoRolloutConfigKeeperClusterScopeStatus) { *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArgoRolloutConfigKeeperClusterScopeStatus. @@ -209,6 +217,13 @@ func (in *ArgoRolloutConfigKeeperSpec) DeepCopy() *ArgoRolloutConfigKeeperSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ArgoRolloutConfigKeeperStatus) DeepCopyInto(out *ArgoRolloutConfigKeeperStatus) { *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArgoRolloutConfigKeeperStatus. diff --git a/config/crd/bases/configkeeper.run.ai_argorolloutconfigkeeperclusterscopes.yaml b/config/crd/bases/configkeeper.run.ai_argorolloutconfigkeeperclusterscopes.yaml index d900c59..08ca343 100644 --- a/config/crd/bases/configkeeper.run.ai_argorolloutconfigkeeperclusterscopes.yaml +++ b/config/crd/bases/configkeeper.run.ai_argorolloutconfigkeeperclusterscopes.yaml @@ -71,10 +71,75 @@ spec: description: ArgoRolloutConfigKeeperClusterScopeStatus defines the observed state of ArgoRolloutConfigKeeperClusterScope properties: - state: - type: string - required: - - state + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array type: object type: object served: true diff --git a/config/crd/bases/configkeeper.run.ai_argorolloutconfigkeepers.yaml b/config/crd/bases/configkeeper.run.ai_argorolloutconfigkeepers.yaml index 71c19cc..ebac448 100644 --- a/config/crd/bases/configkeeper.run.ai_argorolloutconfigkeepers.yaml +++ b/config/crd/bases/configkeeper.run.ai_argorolloutconfigkeepers.yaml @@ -67,10 +67,75 @@ spec: description: ArgoRolloutConfigKeeperStatus defines the observed state of ArgoRolloutConfigKeeper properties: - state: - type: string - required: - - state + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array type: object type: object served: true diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..332a962 --- /dev/null +++ b/go.sum @@ -0,0 +1,240 @@ +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +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.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +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.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +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.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= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +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= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= +github.com/prometheus/common v0.47.0 h1:p5Cz0FNHo7SnWOmWmoRozVcjEp0bIVU8cV7OShpjL1k= +github.com/prometheus/common v0.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +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.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +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/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +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= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= +k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= +k8s.io/apiextensions-apiserver v0.29.3 h1:9HF+EtZaVpFjStakF4yVufnXGPRppWFEQ87qnO91YeI= +k8s.io/apiextensions-apiserver v0.29.3/go.mod h1:po0XiY5scnpJfFizNGo6puNU6Fq6D70UJY2Cb2KwAVc= +k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= +k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= +k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= +k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= +k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo= +k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio= +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-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= +sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +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.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= diff --git a/internal/common/common.go b/internal/common/common.go index 2411897..b70d209 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -6,6 +6,9 @@ import ( "strings" "time" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/run-ai/argo-rollout-config-keeper/internal/tools" "github.com/go-logr/logr" @@ -14,7 +17,6 @@ import ( "github.com/run-ai/argo-rollout-config-keeper/internal/metrics" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -25,7 +27,6 @@ type ArgoRolloutConfigKeeperLabels struct { type ArgoRolloutConfigKeeperCommon struct { client.Client - Scheme *runtime.Scheme Logger logr.Logger Labels *ArgoRolloutConfigKeeperLabels FinalizerName string @@ -33,52 +34,73 @@ type ArgoRolloutConfigKeeperCommon struct { func (r *ArgoRolloutConfigKeeperCommon) ReconcileConfigMaps(ctx context.Context, namespace string, labelSelector map[string]string, ignoredNamespaces map[string]bool) error { defer func() { - metrics.ConfigMapReconcileDuration.Observe(time.Since(time.Now()).Seconds()) + if namespace != "" { + metrics.ConfigMapClusterScopeReconcileDuration.Observe(time.Since(time.Now()).Seconds()) + } else { + metrics.ConfigMapReconcileDuration.Observe(time.Since(time.Now()).Seconds()) + } }() - metrics.ManagedConfigMapCount.Set(0) - if configMaps, err := r.listConfigMaps(ctx, namespace, labelSelector); err != nil { + if namespace != "" { + metrics.ManagedConfigMapClusterScopeCount.Set(0) + } else { + metrics.ManagedConfigMapCount.Set(0) + } + + configMaps, err := r.listConfigMaps(ctx, namespace, labelSelector) + if err != nil { return err + } + if namespace != "" { + metrics.DiscoveredConfigMapClusterScopeCount.Set(float64(len(configMaps.Items))) } else { metrics.DiscoveredConfigMapCount.Set(float64(len(configMaps.Items))) - if configMaps.Items != nil { - for _, c := range configMaps.Items { - r.Logger.Info(fmt.Sprintf("configmap, name: %s", c.Name)) - if _, ok := ignoredNamespaces[c.Namespace]; ok { - r.Logger.Info(fmt.Sprintf("skipping %s configmap, reason: namespace is ignored", c.Name)) - continue - } - if c.Finalizers != nil { - // check if the finalizer is in the finalizers list - finalizerFullName, havingManagedFinalizer := tools.ContainsString(c.GetFinalizers(), r.FinalizerName) + } - if havingManagedFinalizer { - metrics.ManagedConfigMapCount.Inc() - if err := r.finalizerOperation(ctx, &c, finalizerFullName); err != nil { - return err - } - - if latestVersion, err := r.getLatestVersionOfConfig(ctx, strings.Split(finalizerFullName, "/")[1], &c); err != nil { - r.Logger.Error(err, "unable to get latest version of configmap") - return err - } else { - if err := r.ignoreExtraneousOperation(ctx, &c, latestVersion); err != nil { - return err - } - } + if configMaps.Items != nil { + for _, c := range configMaps.Items { + r.Logger.Info(fmt.Sprintf("configmap, name: %s", c.Name)) + if _, ok := ignoredNamespaces[c.Namespace]; ok { + r.Logger.Info(fmt.Sprintf("skipping %s configmap, reason: namespace is ignored", c.Name)) + continue + } + if c.Finalizers != nil { + // check if the finalizer is in the finalizers list + finalizerFullName, havingManagedFinalizer := tools.ContainsString(c.GetFinalizers(), r.FinalizerName) + + if havingManagedFinalizer { + if namespace != "" { + metrics.ManagedConfigMapClusterScopeCount.Inc() } else { - r.Logger.Info(fmt.Sprintf("skipping %s configmap, reason: no manageable finalizer", c.Name)) + metrics.ManagedConfigMapCount.Inc() + } + + if err := r.finalizerOperation(ctx, &c, finalizerFullName); err != nil { + return err + } + + latestVersion, err := r.getLatestVersionOfConfig(ctx, strings.Split(finalizerFullName, "/")[1], &c) + if err != nil { + r.Logger.Error(err, "unable to get latest version of configmap") + return err } - continue + + err = r.ignoreExtraneousOperation(ctx, &c, latestVersion) + if err != nil { + return err + } + } else { + r.Logger.Info(fmt.Sprintf("skipping %s configmap, reason: no manageable finalizer", c.Name)) } - r.Logger.Info(fmt.Sprintf("skipping %s configmap, reason: no finalizers", c.Name)) + continue } + r.Logger.Info(fmt.Sprintf("skipping %s configmap, reason: no finalizers", c.Name)) + } + } else { + if namespace != "" { + r.Logger.Info(fmt.Sprintf("no configmaps found in %s namespace", namespace)) } else { - if namespace != "" { - r.Logger.Info(fmt.Sprintf("no configmaps found in %s namespace", namespace)) - } else { - r.Logger.Info("no configmaps found") - } + r.Logger.Info("no configmaps found") } } @@ -87,52 +109,71 @@ func (r *ArgoRolloutConfigKeeperCommon) ReconcileConfigMaps(ctx context.Context, func (r *ArgoRolloutConfigKeeperCommon) ReconcileSecrets(ctx context.Context, namespace string, labelSelector map[string]string, ignoredNamespaces map[string]bool) error { defer func() { - metrics.SecretReconcileDuration.Observe(time.Since(time.Now()).Seconds()) + if namespace != "" { + metrics.SecretClusterScopeReconcileDuration.Observe(time.Since(time.Now()).Seconds()) + } else { + metrics.SecretReconcileDuration.Observe(time.Since(time.Now()).Seconds()) + } }() - metrics.ManagedSecretCount.Set(0) + if namespace != "" { + metrics.ManagedSecretClusterScopeCount.Set(0) + } else { + metrics.ManagedSecretCount.Set(0) + } - if secrets, err := r.listSecrets(ctx, namespace, labelSelector); err != nil { + secrets, err := r.listSecrets(ctx, namespace, labelSelector) + if err != nil { return err + } + if namespace != "" { + metrics.DiscoveredSecretClusterScopeCount.Set(float64(len(secrets.Items))) } else { metrics.DiscoveredSecretCount.Set(float64(len(secrets.Items))) - if secrets.Items != nil { - for _, s := range secrets.Items { - r.Logger.Info(fmt.Sprintf("secret, name: %s", s.Name)) - if _, ok := ignoredNamespaces[s.Namespace]; ok { - r.Logger.Info(fmt.Sprintf("skipping %s secret, reason: namespace is ignored", s.Name)) - continue - } + } + + if secrets.Items != nil { + for _, s := range secrets.Items { + r.Logger.Info(fmt.Sprintf("secret, name: %s", s.Name)) + if _, ok := ignoredNamespaces[s.Namespace]; ok { + r.Logger.Info(fmt.Sprintf("skipping %s secret, reason: namespace is ignored", s.Name)) + continue + } - if s.Finalizers != nil { - finalizerFullName, havingManagedFinalizer := tools.ContainsString(s.GetFinalizers(), r.FinalizerName) + if s.Finalizers != nil { + finalizerFullName, havingManagedFinalizer := tools.ContainsString(s.GetFinalizers(), r.FinalizerName) - if havingManagedFinalizer { - metrics.ManagedSecretCount.Inc() - if err := r.finalizerOperation(ctx, &s, finalizerFullName); err != nil { - return err - } - - if latestVersion, err := r.getLatestVersionOfConfig(ctx, strings.Split(finalizerFullName, "/")[1], &s); err != nil { - r.Logger.Error(err, "unable to get latest version of secret") - return err - } else { - if err := r.ignoreExtraneousOperation(ctx, &s, latestVersion); err != nil { - return err - } - } + if havingManagedFinalizer { + if namespace != "" { + metrics.ManagedSecretClusterScopeCount.Inc() } else { - r.Logger.Info(fmt.Sprintf("skipping %s secret, reason: no manageable finalizer", s.Name)) + metrics.ManagedSecretCount.Inc() + } + + if err := r.finalizerOperation(ctx, &s, finalizerFullName); err != nil { + return err + } + + latestVersion, err := r.getLatestVersionOfConfig(ctx, strings.Split(finalizerFullName, "/")[1], &s) + if err != nil { + r.Logger.Error(err, "unable to get latest version of secret") + return err + } + err = r.ignoreExtraneousOperation(ctx, &s, latestVersion) + if err != nil { + return err } - continue + } else { + r.Logger.Info(fmt.Sprintf("skipping %s secret, reason: no manageable finalizer", s.Name)) } - r.Logger.Info(fmt.Sprintf("skipping %s secret, reason: no finalizers", s.Name)) + continue } + r.Logger.Info(fmt.Sprintf("skipping %s secret, reason: no finalizers", s.Name)) + } + } else { + if namespace != "" { + r.Logger.Info(fmt.Sprintf("no secrets found in %s namespace", namespace)) } else { - if namespace != "" { - r.Logger.Info(fmt.Sprintf("no secrets found in %s namespace", namespace)) - } else { - r.Logger.Info("no secrets found") - } + r.Logger.Info("no secrets found") } } @@ -155,7 +196,11 @@ func (r *ArgoRolloutConfigKeeperCommon) finalizerOperation(ctx context.Context, if err != nil { r.Logger.Error(err, "unable to remove finalizer from configmap", "name", t.Name) } - metrics.ManagedConfigMapCount.Dec() + if t.Namespace != "" { + metrics.ManagedConfigMapClusterScopeCount.Dec() + } else { + metrics.ManagedConfigMapCount.Dec() + } } return err case *corev1.Secret: @@ -171,7 +216,11 @@ func (r *ArgoRolloutConfigKeeperCommon) finalizerOperation(ctx context.Context, if err != nil { r.Logger.Error(err, "unable to remove finalizer from secret", "name", t.Name) } - metrics.ManagedSecretCount.Dec() + if t.Namespace != "" { + metrics.ManagedSecretClusterScopeCount.Dec() + } else { + metrics.ManagedSecretCount.Dec() + } } return err default: @@ -255,7 +304,7 @@ func (r *ArgoRolloutConfigKeeperCommon) checkIfFinalizerInUse(ctx context.Contex for _, replicaSet := range replicaSets.Items { replicaNum := int32(0) - if replicaSet.Labels[r.Labels.AppLabel] == appLabelValue && replicaSet.Labels[r.Labels.AppVersionLabel] == chartVersion && (*replicaSet.Spec.Replicas != replicaNum || replicaSet.Status.Replicas != replicaNum) { + if *replicaSet.Spec.Replicas != replicaNum || replicaSet.Status.Replicas != replicaNum { r.Logger.Info(fmt.Sprintf("finalizer in use by %s replicaset", replicaSet.Name)) return true, nil } @@ -278,7 +327,6 @@ func (r *ArgoRolloutConfigKeeperCommon) getLatestVersionOfReplicaSet(ctx context var latestVersion *goversion.Version for _, replicaSet := range replicaSets.Items { - if val, ok := replicaSet.Labels[r.Labels.AppVersionLabel]; ok { ver, err := goversion.NewVersion(val) if err != nil { @@ -296,39 +344,6 @@ func (r *ArgoRolloutConfigKeeperCommon) getLatestVersionOfReplicaSet(ctx context return latestVersion, nil } -func (r *ArgoRolloutConfigKeeperCommon) UpdateStatus(ctx context.Context, T interface{}, status string) error { - switch t := T.(type) { - case *keeperv1alpha1.ArgoRolloutConfigKeeper: - if t.Status.State != status { - r.Logger.Info(fmt.Sprintf("updating %s ArgoRolloutConfigKeeper status from %s to %s", t.Name, t.Status.State, status)) - t.Status.State = status - err := r.Status().Update(ctx, t) - if err != nil { - r.Logger.Error(err, "unable to update status") - return err - } - // the sleep is to allow the status to be updated before the next reconcile - time.Sleep(1 * time.Second) - } - return nil - case *keeperv1alpha1.ArgoRolloutConfigKeeperClusterScope: - if t.Status.State != status { - r.Logger.Info(fmt.Sprintf("updating %s ArgoRolloutConfigKeeper status from %s to %s", t.Name, t.Status.State, status)) - t.Status.State = status - err := r.Status().Update(ctx, t) - if err != nil { - r.Logger.Error(err, "unable to update status") - return err - } - // the sleep is to allow the status to be updated before the next reconcile - time.Sleep(1 * time.Second) - } - return nil - default: - return fmt.Errorf("unsupported type: %T", T) - } -} - func (r *ArgoRolloutConfigKeeperCommon) listConfigMaps(ctx context.Context, namespace string, labelSelector map[string]string) (*corev1.ConfigMapList, error) { configmaps := &corev1.ConfigMapList{} @@ -374,19 +389,19 @@ func (r *ArgoRolloutConfigKeeperCommon) getLatestVersionOfConfig(ctx context.Con } if latestVersion == nil { - if configMaps, err := r.listConfigMaps(ctx, t.Namespace, labels); err != nil { + configMaps, err := r.listConfigMaps(ctx, t.Namespace, labels) + if err != nil { return nil, err - } else { - for _, c := range configMaps.Items { - if val, ok := c.Labels[r.Labels.AppVersionLabel]; ok { - ver, err := goversion.NewVersion(val) - if err != nil { - r.Logger.Error(err, "unable to parse version") - return nil, err - } - if latestVersion == nil || ver.GreaterThan(latestVersion) { - latestVersion = ver - } + } + for _, c := range configMaps.Items { + if val, ok := c.Labels[r.Labels.AppVersionLabel]; ok { + ver, err := goversion.NewVersion(val) + if err != nil { + r.Logger.Error(err, "unable to parse version") + return nil, err + } + if latestVersion == nil || ver.GreaterThan(latestVersion) { + latestVersion = ver } } } @@ -405,19 +420,19 @@ func (r *ArgoRolloutConfigKeeperCommon) getLatestVersionOfConfig(ctx context.Con if latestVersion == nil { r.Logger.Info("could not get latest version from replicaset, trying to get from secrets") - if secrets, err := r.listSecrets(ctx, t.Namespace, labels); err != nil { + secrets, err := r.listSecrets(ctx, t.Namespace, labels) + if err != nil { return nil, err - } else { - for _, c := range secrets.Items { - if val, ok := c.Labels[r.Labels.AppVersionLabel]; ok { - ver, err := goversion.NewVersion(val) - if err != nil { - r.Logger.Error(err, "unable to parse version") - return nil, err - } - if latestVersion == nil || ver.GreaterThan(latestVersion) { - latestVersion = ver - } + } + for _, c := range secrets.Items { + if val, ok := c.Labels[r.Labels.AppVersionLabel]; ok { + ver, err := goversion.NewVersion(val) + if err != nil { + r.Logger.Error(err, "unable to parse version") + return nil, err + } + if latestVersion == nil || ver.GreaterThan(latestVersion) { + latestVersion = ver } } } @@ -427,3 +442,22 @@ func (r *ArgoRolloutConfigKeeperCommon) getLatestVersionOfConfig(ctx context.Con return nil, fmt.Errorf("unsupported type: %T", T) } } + +func (r *ArgoRolloutConfigKeeperCommon) UpdateCondition(ctx context.Context, T interface{}, condition metav1.Condition) error { + switch t := T.(type) { + case *keeperv1alpha1.ArgoRolloutConfigKeeper: + changed := meta.SetStatusCondition(&t.Status.Conditions, condition) + if !changed { + return nil + } + return r.Status().Update(ctx, t) + case *keeperv1alpha1.ArgoRolloutConfigKeeperClusterScope: + changed := meta.SetStatusCondition(&t.Status.Conditions, condition) + if !changed { + return nil + } + return r.Status().Update(ctx, t) + default: + return fmt.Errorf("unsupported type: %T", T) + } +} diff --git a/internal/controller/argorolloutconfigkeeper_controller.go b/internal/controller/argorolloutconfigkeeper_controller.go index df8ec19..6c5c345 100644 --- a/internal/controller/argorolloutconfigkeeper_controller.go +++ b/internal/controller/argorolloutconfigkeeper_controller.go @@ -18,9 +18,10 @@ package controller import ( "context" - "fmt" "time" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/go-logr/logr" keeperv1alpha1 "github.com/run-ai/argo-rollout-config-keeper/api/v1alpha1" "github.com/run-ai/argo-rollout-config-keeper/internal/common" @@ -39,15 +40,6 @@ type ArgoRolloutConfigKeeperReconciler struct { logger logr.Logger } -const ( - ArgoRolloutConfigStateInitializing = "initializing" - ArgoRolloutConfigStateReconcilingConfigmapsInNamespace = "reconciling configmaps in namespace %s" - ArgoRolloutConfigStateReconcilingSecretsInNamespace = "reconciling secrets in namespace %s" - ArgoRolloutConfigStateReconcilingConfigmaps = "reconciling configmaps in all namespaces" - ArgoRolloutConfigStateReconcilingSecrets = "reconciling secrets in all namespaces" - ArgoRolloutConfigStateFinished = "finished" -) - //+kubebuilder:rbac:groups=configkeeper.run.ai,resources=argorolloutconfigkeepers,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=configkeeper.run.ai,resources=argorolloutconfigkeepers/status,verbs=get;update;patch //+kubebuilder:rbac:groups=configkeeper.run.ai,resources=argorolloutconfigkeepers/finalizers,verbs=update @@ -61,16 +53,23 @@ func (r *ArgoRolloutConfigKeeperReconciler) Reconcile(ctx context.Context, req c }() configKeeperCommon := common.ArgoRolloutConfigKeeperCommon{ Client: r.Client, - Scheme: r.Scheme, Logger: r.logger, } configKeeper := &keeperv1alpha1.ArgoRolloutConfigKeeper{} if err := r.Get(ctx, req.NamespacedName, configKeeper); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, client.IgnoreNotFound(err) + return ctrl.Result{}, client.IgnoreNotFound(err) } - if err := configKeeperCommon.UpdateStatus(ctx, configKeeper, ArgoRolloutConfigStateInitializing); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, err + + argoRolloutConfigStateInitializing := metav1.Condition{ + Type: ArgoRolloutConfigStateInitializing, + Status: metav1.ConditionFalse, + Reason: "ArgoRolloutConfigKeeperInitializing", + Message: "ArgoRolloutConfigKeeper is initializing", + } + + if err := configKeeperCommon.UpdateCondition(ctx, configKeeper, argoRolloutConfigStateInitializing); err != nil { + return ctrl.Result{}, err } configKeeperCommon.Labels = &common.ArgoRolloutConfigKeeperLabels{ @@ -79,8 +78,15 @@ func (r *ArgoRolloutConfigKeeperReconciler) Reconcile(ctx context.Context, req c } configKeeperCommon.FinalizerName = configKeeper.Spec.FinalizerName - if err := configKeeperCommon.UpdateStatus(ctx, configKeeper, fmt.Sprintf(ArgoRolloutConfigStateReconcilingConfigmapsInNamespace, req.Namespace)); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, err + argoRolloutConfigStateReady := metav1.Condition{ + Type: ArgoRolloutConfigStateReady, + Status: metav1.ConditionTrue, + Reason: "ArgoRolloutConfigKeeperReady", + Message: "ArgoRolloutConfigKeeper is ready", + } + + if err := configKeeperCommon.UpdateCondition(ctx, configKeeper, argoRolloutConfigStateReady); err != nil { + return ctrl.Result{}, err } labelSelector := map[string]string{} @@ -89,21 +95,14 @@ func (r *ArgoRolloutConfigKeeperReconciler) Reconcile(ctx context.Context, req c } if err := configKeeperCommon.ReconcileConfigMaps(ctx, req.Namespace, labelSelector, map[string]bool{}); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, client.IgnoreNotFound(err) - } - - if err := configKeeperCommon.UpdateStatus(ctx, configKeeper, fmt.Sprintf(ArgoRolloutConfigStateReconcilingSecretsInNamespace, req.Namespace)); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, err + return ctrl.Result{}, client.IgnoreNotFound(err) } // need to list all secrets in namespace if err := configKeeperCommon.ReconcileSecrets(ctx, req.Namespace, labelSelector, map[string]bool{}); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, client.IgnoreNotFound(err) + return ctrl.Result{}, client.IgnoreNotFound(err) } - if err := configKeeperCommon.UpdateStatus(ctx, configKeeper, ArgoRolloutConfigStateFinished); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, err - } return ctrl.Result{RequeueAfter: 1 * time.Minute}, nil } diff --git a/internal/controller/argorolloutconfigkeeper_controller_test.go b/internal/controller/argorolloutconfigkeeper_controller_test.go index 670280f..691d548 100644 --- a/internal/controller/argorolloutconfigkeeper_controller_test.go +++ b/internal/controller/argorolloutconfigkeeper_controller_test.go @@ -19,6 +19,7 @@ package controller import ( "context" "fmt" + "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -406,16 +407,19 @@ func manageReplicas(ctx context.Context, namespace, operation string, replicaNum // create replica Expect(k8sClient.Create(ctx, replica)).To(Succeed()) Expect(k8sClient.Create(ctx, replicaPreview)).To(Succeed()) + time.Sleep(2 * time.Second) break case "delete": // delete replica Expect(k8sClient.Delete(ctx, replica)).To(Succeed()) Expect(k8sClient.Delete(ctx, replicaPreview)).To(Succeed()) + time.Sleep(2 * time.Second) break case "update": // update replica Expect(k8sClient.Update(ctx, replica)).To(Succeed()) Expect(k8sClient.Update(ctx, replicaPreview)).To(Succeed()) + time.Sleep(2 * time.Second) break default: panic("Invalid operation") diff --git a/internal/controller/argorolloutconfigkeeperclusterscope_controller.go b/internal/controller/argorolloutconfigkeeperclusterscope_controller.go index 5daa2cb..ffbab68 100644 --- a/internal/controller/argorolloutconfigkeeperclusterscope_controller.go +++ b/internal/controller/argorolloutconfigkeeperclusterscope_controller.go @@ -20,6 +20,8 @@ import ( "context" "time" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/go-logr/logr" configkeeperv1alpha1 "github.com/run-ai/argo-rollout-config-keeper/api/v1alpha1" "github.com/run-ai/argo-rollout-config-keeper/internal/common" @@ -48,22 +50,28 @@ type ArgoRolloutConfigKeeperClusterScopeReconciler struct { func (r *ArgoRolloutConfigKeeperClusterScopeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { r.logger = log.FromContext(ctx) defer func() { - metrics.OverallReconcileDuration.Observe(time.Since(time.Now()).Seconds()) + metrics.OverallClusterScopeReconcileDuration.Observe(time.Since(time.Now()).Seconds()) }() configKeeperCommon := common.ArgoRolloutConfigKeeperCommon{ Client: r.Client, - Scheme: r.Scheme, Logger: r.logger, } configKeeperClusterScope := &configkeeperv1alpha1.ArgoRolloutConfigKeeperClusterScope{} if err := r.Get(ctx, req.NamespacedName, configKeeperClusterScope); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, client.IgnoreNotFound(err) + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + argoRolloutConfigStateClusterScopeInitializing := metav1.Condition{ + Type: ArgoRolloutConfigStateInitializing, + Status: metav1.ConditionFalse, + Reason: "ArgoRolloutConfigKeeperClusterScopeInitializing", + Message: "ArgoRolloutConfigKeeperClusterScope is initializing", } - if err := configKeeperCommon.UpdateStatus(ctx, configKeeperClusterScope, ArgoRolloutConfigStateInitializing); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, err + if err := configKeeperCommon.UpdateCondition(ctx, configKeeperClusterScope, argoRolloutConfigStateClusterScopeInitializing); err != nil { + return ctrl.Result{}, err } configKeeperCommon.Labels = &common.ArgoRolloutConfigKeeperLabels{ @@ -72,8 +80,15 @@ func (r *ArgoRolloutConfigKeeperClusterScopeReconciler) Reconcile(ctx context.Co } configKeeperCommon.FinalizerName = configKeeperClusterScope.Spec.FinalizerName - if err := configKeeperCommon.UpdateStatus(ctx, configKeeperClusterScope, ArgoRolloutConfigStateReconcilingConfigmaps); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, err + argoRolloutConfigStateClusterScopeReady := metav1.Condition{ + Type: ArgoRolloutConfigStateReady, + Status: metav1.ConditionTrue, + Reason: "ArgoRolloutConfigKeeperClusterScopeReady", + Message: "ArgoRolloutConfigKeeperClusterScope is ready", + } + + if err := configKeeperCommon.UpdateCondition(ctx, configKeeperClusterScope, argoRolloutConfigStateClusterScopeReady); err != nil { + return ctrl.Result{}, err } labelSelector := map[string]string{} @@ -84,20 +99,14 @@ func (r *ArgoRolloutConfigKeeperClusterScopeReconciler) Reconcile(ctx context.Co ignoredNamespaces := tools.CreateMapFromStringList(configKeeperClusterScope.Spec.IgnoredNamespaces) if err := configKeeperCommon.ReconcileConfigMaps(ctx, "", labelSelector, ignoredNamespaces); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, client.IgnoreNotFound(err) + return ctrl.Result{}, client.IgnoreNotFound(err) } - if err := configKeeperCommon.UpdateStatus(ctx, configKeeperClusterScope, ArgoRolloutConfigStateReconcilingSecrets); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, err - } // need to list all secrets in namespace if err := configKeeperCommon.ReconcileSecrets(ctx, "", labelSelector, ignoredNamespaces); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, client.IgnoreNotFound(err) + return ctrl.Result{}, client.IgnoreNotFound(err) } - if err := configKeeperCommon.UpdateStatus(ctx, configKeeperClusterScope, ArgoRolloutConfigStateFinished); err != nil { - return ctrl.Result{RequeueAfter: 1 * time.Minute}, err - } return ctrl.Result{RequeueAfter: 1 * time.Minute}, nil } diff --git a/internal/controller/const.go b/internal/controller/const.go new file mode 100644 index 0000000..a790f72 --- /dev/null +++ b/internal/controller/const.go @@ -0,0 +1,6 @@ +package controller + +const ( + ArgoRolloutConfigStateInitializing = "Initializing" + ArgoRolloutConfigStateReady = "Ready" +) diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index f841c3e..1745af2 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -41,6 +41,41 @@ var ( Name: "argo_rollout_config_keeper_overall_reconcile_duration_seconds", Help: "Time taken to reconcile overall process", }) + ManagedConfigMapClusterScopeCount = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "argo_rollout_config_keeper_managed_configmap_clusterscope_count", + Help: "Number of managed configmaps by argo rollout config keeper cluster scope operator", + }) + ManagedSecretClusterScopeCount = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "argo_rollout_config_keeper_managed_secret_clusterscope_count", + Help: "Number of managed secrets by argo rollout config keeper cluster scope operator", + }) + DiscoveredConfigMapClusterScopeCount = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "argo_rollout_config_keeper_discovered_configmap_clusterscope_count", + Help: "Number of discovered configmaps by argo rollout config keeper cluster scope operator", + }) + DiscoveredSecretClusterScopeCount = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "argo_rollout_config_keeper_discovered_secret_clusterscope_count", + Help: "Number of discovered secrets by argo rollout config keeper cluster scope operator", + }) + ConfigMapClusterScopeReconcileDuration = prometheus.NewHistogram( + prometheus.HistogramOpts{ + Name: "argo_rollout_config_keeper_configmap_clusterscope_reconcile_duration_seconds", + Help: "Time taken to reconcile configmaps by argo rollout config keeper cluster scope operator", + }) + SecretClusterScopeReconcileDuration = prometheus.NewHistogram( + prometheus.HistogramOpts{ + Name: "argo_rollout_config_keeper_secret_clusterscope_reconcile_duration_seconds", + Help: "Time taken to reconcile secrets by argo rollout config keeper cluster scope operator", + }) + OverallClusterScopeReconcileDuration = prometheus.NewHistogram( + prometheus.HistogramOpts{ + Name: "argo_rollout_config_keeper_overall_clusterscope_reconcile_duration_seconds", + Help: "Time taken to reconcile overall process by argo rollout config keeper cluster scope operator", + }) ) func init() {