func reconcileEsUserSecret()

in pkg/controller/association/user.go [79:170]


func reconcileEsUserSecret(
	ctx context.Context,
	c k8s.Client,
	association commonv1.Association,
	labels map[string]string,
	userRoles string,
	userObjectSuffix string,
	es esv1.Elasticsearch,
) error {
	span, ctx := apm.StartSpan(ctx, "reconcile_es_user", tracing.SpanTypeApp)
	defer span.End()

	// Add the Elasticsearch name, this is only intended to help the user to filter on these resources
	labels[eslabel.ClusterNameLabelName] = es.Name

	secKey := secretKey(association, userObjectSuffix)
	usrKey := UserKey(association, es.Namespace, userObjectSuffix)
	expectedSecret := corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      secKey.Name,
			Namespace: secKey.Namespace,
			Labels:    commonlabels.AddCredentialsLabel(labels),
		},
		Data: map[string][]byte{},
	}

	var password []byte
	// reuse the existing password if there's one
	var existingSecret corev1.Secret
	err := c.Get(ctx, k8s.ExtractNamespacedName(&expectedSecret), &existingSecret)
	if err != nil && !apierrors.IsNotFound(err) {
		return err
	}
	if existingPassword, exists := existingSecret.Data[usrKey.Name]; exists {
		password = existingPassword
	} else {
		password = common.FixedLengthRandomPasswordBytes()
	}
	expectedSecret.Data[usrKey.Name] = password

	if _, err := reconciler.ReconcileSecret(ctx, c, expectedSecret, association.Associated()); err != nil {
		return err
	}

	// analogous to the association secret: a user Secret goes on the Elasticsearch side of the association
	// we apply the ES cluster labels ("user belongs to that ES cluster")
	// and the association label ("for that association object")

	// merge the association labels provided by the controller with the one needed for a user
	userLabels := esuser.AssociatedUserLabels(es)
	for key, value := range labels {
		userLabels[key] = value
	}

	expectedEsUser := corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      usrKey.Name,
			Namespace: usrKey.Namespace,
			Labels:    userLabels,
		},
		Data: map[string][]byte{
			esuser.UserNameField:  []byte(usrKey.Name),
			esuser.UserRolesField: []byte(userRoles),
		},
	}

	var existingUserSecret corev1.Secret
	if err := c.Get(ctx, k8s.ExtractNamespacedName(&expectedEsUser), &existingUserSecret); err != nil && !apierrors.IsNotFound(err) {
		return err
	}

	// reuse the existing hash if valid
	var bcryptHash []byte
	if existingHash, exists := existingUserSecret.Data[esuser.PasswordHashField]; exists {
		if bcrypt.CompareHashAndPassword(existingHash, password) == nil {
			bcryptHash = existingHash
		}
	}

	if bcryptHash == nil {
		bcryptHash, err = bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
		if err != nil {
			return err
		}
	}

	expectedEsUser.Data[esuser.PasswordHashField] = bcryptHash

	owner := es // user is owned by the es resource in es namespace
	_, err = reconciler.ReconcileSecret(ctx, c, expectedEsUser, &owner)
	return err
}