This proposal is about adding the ability to filter the automatically assigned labels.
When User Operator reconciles a KafkaUser
, it also creates an associated Secret, where it automatically adds the label
app.kubernetes.io/instance: <username>
.
Such a label could be used for many uses, and different users have various requirements and expectations.
Nevertheless, it could also lead to undesirable scenarios (e.g., repeated deletion and re-creation of specific Secret).
Therefore, we propose to make exclusion of labels configurable.
Presently, when a user attempts to create a KafkaUser
defined as follows:
apiVersion: kafka.strimzi.io/v1beta2
kind: KafkaUser
metadata:
name: my-user
labels:
strimzi.io/cluster: my-cluster
spec:
authentication:
type: tls
The User Operator then creates an associated Kubernetes Secret
.
kind: Secret
metadata:
labels:
app.kubernetes.io/instance: my-user
app.kubernetes.io/managed-by: strimzi-user-operator
app.kubernetes.io/name: strimzi-user-operator
app.kubernetes.io/part-of: strimzi-my-user
strimzi.io/cluster: my-cluster
strimzi.io/kind: KafkaUser
name: my-user
namespace: myproject
...
We can see that User Operator automatically adds the app.kubernetes.io/instance: my-user
label.
If we do not want the label that has been assigned, the only way to get rid of it is to filter it using regexes.
This proposal suggests adding configurable exclusion of labels.
We can implement this feature similarly to issue 4394.
We can create an environment variable and inject the value through the Kafka
custom resource.
Specifically, in the spec.entityOperator.template.userOperatorContainer.env
.
For instance, we can have the following Kafka
resource:
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
name: my-cluster
spec:
...
entityOperator:
template:
userOperatorContainer:
env:
name: STRIMZI_LABELS_EXCLUSION_PATTERN
value: "^app.kubernetes.io/.*$"
topicOperator: {}
userOperator: {}
Then, we would obtain a value from the environment variable and parse it in the UserOperatorConfig
class.
Based on the environment variable value, (a) when env
is not specified, we do not exclude any label from the secret
(b) when env
is specified by a regex, we filter and remove those labels which match the regex.
As we mentioned details about the UserOperatorConfig
class, we will elaborate more on the specific details in this section.
Moreover, we will obtain the environment variable inside the fromMap
method used to construct the class.
public static UserOperatorConfig fromMap(Map<String, String> map) {
...
//
String strimziLabelsExclusionPatternEnvVar = map.get(UserOperatorConfig.STRIMZI_LABELS_EXCLUSION_PATTERN);
if (strimziLabelsExclusionPattern != null) {
// note that we will compile such regex into FSM only once here and thus eliminate workload inside KafkaUserModel
strimziLabelsExclusionPattern = Pattern.compile(strimziLabelsExclusionPatternEnvVar);
}
...
}
The exclusion part will be placed in the KafkaUserModel
class.
Moreover, we would need to get the value of the environment variable to such a class.
In the UserOperatorConfig
class, we will obtain the environment variable value, and then in the KafkaUserOperator
class, specifically in the following method:
protected Future<KafkaUserStatus> createOrUpdate(Reconciliation reconciliation, KafkaUser resource) {
KafkaUserModel user;
KafkaUserStatus userStatus = new KafkaUserStatus();
try {
user = KafkaUserModel.fromCrd(resource, config.getSecretPrefix(), config.isAclsAdminApiSupported(),config.isKraftEnabled(),
config.getStrimziLabelsExclusionPattern); // <-- this one we will inject into KafkaUserModel class
} catch (Exception e) {
StatusUtils.setStatusConditionAndObservedGeneration(resource, userStatus, Future.failedFuture(e));
return Future.failedFuture(new ReconciliationException(userStatus, e));
}
...
}
Then, we can use the environment variable value to store it as an instance attribute of KafkaUserModel
and implement pre-processing of labels in the
following method:
protected Secret createSecret(Map<String, String> data) {
final Map<String, String> labels = Util.mergeLabelsOrAnnotations(labels.toMap(), templateSecretLabels);
// here, we have to do pre-processing (i.e., filtering) of labels by exclusion pattern
// filter by value of instance variable `this.getStrimziLabelsExclusionPattern`
return return new SecretBuilder()
.withNewMetadata()
.withName(getSecretName())
.withNamespace(namespace)
.withLabels(labels)
.withAnnotations(Util.mergeLabelsOrAnnotations(null, templateSecretAnnotations))
.withOwnerReferences(createOwnerReference())
.endMetadata()
.withType("Opaque")
.withData(data)
.build();
}
This proposal does not change any of the existing CRDs or the Kubernetes secrets that are being created. The user only needs to modify either User Operator deployment in the case of a standalone approach or Kafka's custom resource to use such a feature.
- We considered the alternative to remove such labels entirely simply. However, this could lead to un-excepted behaviour (i.e., breaking any users relying on them).