Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 57 additions & 38 deletions controllers/dashboards/dashboard_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
persesv1 "github.com/perses/perses/pkg/model/api/v1"
persesv1Common "github.com/perses/perses/pkg/model/api/v1/common"

v1 "github.com/perses/perses/pkg/client/api/v1"

logger "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -86,14 +88,29 @@ func (r *PersesDashboardReconciler) reconcileDashboardInAllInstances(ctx context
}

func (r *PersesDashboardReconciler) syncPersesDashboard(ctx context.Context, perses persesv1alpha2.Perses, dashboard *persesv1alpha2.PersesDashboard) (*ctrl.Result, common.ConditionStatusReason, error) {
persesClient, err := r.ClientFactory.CreateClient(ctx, r.APIReader, perses)

clients, err := r.ClientFactory.CreateClientsForAllPods(ctx, r.APIReader, perses)
if err != nil {
dlog.WithError(err).Error("Failed to create perses rest client")
dlog.WithError(err).Error("Failed to create perses rest clients")
return subreconciler.RequeueWithErrorAndReason(err, common.ReasonConnectionFailed)
}

_, err = persesClient.Project().Get(dashboard.Namespace)
var errs []error
for _, persesClient := range clients {
if err := r.syncDashboardToClient(ctx, persesClient, dashboard); err != nil {
errs = append(errs, err)
}
}

if len(errs) > 0 {
return subreconciler.RequeueWithErrorAndReason(errors.Join(errs...), common.ReasonBackendError)
}

res, err := subreconciler.ContinueReconciling()
return res, "", err
}

func (r *PersesDashboardReconciler) syncDashboardToClient(ctx context.Context, persesClient v1.ClientInterface, dashboard *persesv1alpha2.PersesDashboard) error {
_, err := persesClient.Project().Get(dashboard.Namespace)

if err != nil {
if errors.Is(err, perseshttp.RequestNotFoundError) {
Expand All @@ -111,13 +128,13 @@ func (r *PersesDashboardReconciler) syncPersesDashboard(ctx context.Context, per

if err != nil {
dlog.WithError(err).Errorf("Failed to create perses project: %s", dashboard.Namespace)
return subreconciler.RequeueWithErrorAndReason(err, common.ReasonBackendError)
return err
}

dlog.Infof("Project created: %s", dashboard.Namespace)
} else {
dlog.WithError(err).Errorf("project error: %s", dashboard.Namespace)
return subreconciler.RequeueWithErrorAndReason(err, common.ReasonBackendError)
return err
}
}

Expand All @@ -137,33 +154,24 @@ func (r *PersesDashboardReconciler) syncPersesDashboard(ctx context.Context, per
if err != nil {
if errors.Is(err, perseshttp.RequestNotFoundError) {
_, err = persesClient.Dashboard(dashboard.Namespace).Create(persesDashboard)

if err != nil {
dlog.WithError(err).Errorf("Failed to create dashboard: %s", dashboard.Name)
return subreconciler.RequeueWithErrorAndReason(err, common.ReasonBackendError)
return err
}

dlog.Infof("Dashboard created: %s", dashboard.Name)

res, err := subreconciler.ContinueReconciling()
return res, "", err
}

return subreconciler.RequeueWithErrorAndReason(err, common.ReasonBackendError)
} else {
_, err = persesClient.Dashboard(dashboard.Namespace).Update(persesDashboard)

if err != nil {
dlog.WithError(err).Errorf("Failed to update dashboard: %s", dashboard.Name)

return subreconciler.RequeueWithErrorAndReason(err, common.ReasonBackendError)
return nil
}
return err
}

dlog.Infof("Dashboard updated: %s", dashboard.Name)
_, err = persesClient.Dashboard(dashboard.Namespace).Update(persesDashboard)
if err != nil {
dlog.WithError(err).Errorf("Failed to update dashboard: %s", dashboard.Name)
return err
}

res, err := subreconciler.ContinueReconciling()
return res, "", err
dlog.Infof("Dashboard updated: %s", dashboard.Name)
return nil
}

func (r *PersesDashboardReconciler) deleteDashboardInAllInstances(ctx context.Context, _ ctrl.Request, dashbboardNamespace string, dashboardName string) (*ctrl.Result, error) {
Expand All @@ -190,35 +198,46 @@ func (r *PersesDashboardReconciler) deleteDashboardInAllInstances(ctx context.Co
}

func (r *PersesDashboardReconciler) deleteDashboard(ctx context.Context, perses persesv1alpha2.Perses, dashboardNamespace string, dashboardName string) (*ctrl.Result, error) {
persesClient, err := r.ClientFactory.CreateClient(ctx, r.APIReader, perses)

clients, err := r.ClientFactory.CreateClientsForAllPods(ctx, r.APIReader, perses)
if err != nil {
dlog.WithError(err).Error("Failed to create perses rest client")
dlog.WithError(err).Error("Failed to create perses rest clients")
return subreconciler.RequeueWithError(err)
}

_, err = persesClient.Project().Get(dashboardNamespace)
var errs []error
for _, persesClient := range clients {
if err := r.deleteDashboardFromClient(persesClient, dashboardNamespace, dashboardName); err != nil {
errs = append(errs, err)
}
}

if len(errs) > 0 {
return subreconciler.RequeueWithError(errors.Join(errs...))
}

return subreconciler.ContinueReconciling()
}

func (r *PersesDashboardReconciler) deleteDashboardFromClient(persesClient v1.ClientInterface, dashboardNamespace string, dashboardName string) error {
_, err := persesClient.Project().Get(dashboardNamespace)
if err != nil {
if errors.Is(err, perseshttp.RequestNotFoundError) {
return nil
}
dlog.WithError(err).Errorf("project error: %s", dashboardNamespace)

return subreconciler.RequeueWithError(err)
return err
}

err = persesClient.Dashboard(dashboardNamespace).Delete(dashboardName)

// Ignore NotFound — the resource may have already been deleted from Perses directly.
// Any other error means the delete failed and should be retried.
if err != nil {
if errors.Is(err, perseshttp.RequestNotFoundError) {
dlog.Infof("Dashboard not found: %s", dashboardName)
return subreconciler.ContinueReconciling()
return nil
}
dlog.WithError(err).Errorf("Failed to delete dashboard: %s", dashboardName)
return subreconciler.RequeueWithError(err)
return err
}

dlog.Infof("Dashboard deleted: %s", dashboardName)

return subreconciler.ContinueReconciling()
return nil
}
1 change: 1 addition & 0 deletions controllers/dashboards/persesdashboard_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ var log = logger.WithField("module", "perses_dashboards_controller")
// +kubebuilder:rbac:groups=perses.dev,resources=persesdashboards,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=perses.dev,resources=persesdashboards/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=perses.dev,resources=persesdashboards/finalizers,verbs=update
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch
func (r *PersesDashboardReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
start := time.Now()
objKey := req.String()
Expand Down
99 changes: 57 additions & 42 deletions controllers/datasources/datasource_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,29 @@ func (r *PersesDatasourceReconciler) reconcileDatasourcesInAllInstances(ctx cont
}

func (r *PersesDatasourceReconciler) syncPersesDatasource(ctx context.Context, perses persesv1alpha2.Perses, datasource *persesv1alpha2.PersesDatasource) (*ctrl.Result, persescommon.ConditionStatusReason, error) {
persesClient, err := r.ClientFactory.CreateClient(ctx, r.APIReader, perses)

clients, err := r.ClientFactory.CreateClientsForAllPods(ctx, r.APIReader, perses)
if err != nil {
dlog.WithError(err).Error("Failed to create perses rest client")
dlog.WithError(err).Error("Failed to create perses rest clients")
return subreconciler.RequeueWithErrorAndReason(err, persescommon.ReasonConnectionFailed)
}

_, err = persesClient.Project().Get(datasource.Namespace)
var errs []error
for _, persesClient := range clients {
if err := r.syncDatasourceToClient(ctx, persesClient, datasource); err != nil {
errs = append(errs, err)
}
}

if len(errs) > 0 {
return subreconciler.RequeueWithErrorAndReason(errors.Join(errs...), persescommon.ReasonBackendError)
}
Comment on lines +105 to +107

res, err := subreconciler.ContinueReconciling()
return res, "", err
}

func (r *PersesDatasourceReconciler) syncDatasourceToClient(ctx context.Context, persesClient v1.ClientInterface, datasource *persesv1alpha2.PersesDatasource) error {
_, err := persesClient.Project().Get(datasource.Namespace)

if err != nil {
if errors.Is(err, perseshttp.RequestNotFoundError) {
Expand All @@ -114,22 +129,21 @@ func (r *PersesDatasourceReconciler) syncPersesDatasource(ctx context.Context, p

if err != nil {
dlog.WithError(err).Errorf("Failed to create perses project: %s", datasource.Namespace)
return subreconciler.RequeueWithErrorAndReason(err, persescommon.ReasonBackendError)
return err
}

dlog.Infof("Project created: %s", datasource.Namespace)
} else {
dlog.WithError(err).Errorf("project error: %s", datasource.Namespace)
return subreconciler.RequeueWithErrorAndReason(err, persescommon.ReasonBackendError)
return err
}
}

// create a secret holding the secret configuration so the datasource can reference it
if persescommon.HasSecretConfig(datasource.Spec.Client) {
_, reason, err := r.syncPersesSecret(ctx, persesClient, datasource)
_, _, err := r.syncPersesSecret(ctx, persesClient, datasource)
if err != nil {
dlog.WithError(err).Errorf("Failed to create datasource secret: %s", datasource.Name)
return subreconciler.RequeueWithErrorAndReason(err, reason)
return err
}
Comment on lines 142 to 147
}

Expand All @@ -149,32 +163,24 @@ func (r *PersesDatasourceReconciler) syncPersesDatasource(ctx context.Context, p
if err != nil {
if errors.Is(err, perseshttp.RequestNotFoundError) {
_, err = persesClient.Datasource(datasource.Namespace).Create(datasourceWithName)

if err != nil {
dlog.WithError(err).Errorf("Failed to create datasource: %s", datasource.Name)
return subreconciler.RequeueWithErrorAndReason(err, persescommon.ReasonBackendError)
return err
}

dlog.Infof("Datasource created: %s", datasource.Name)

res, err := subreconciler.ContinueReconciling()
return res, "", err
}

return subreconciler.RequeueWithErrorAndReason(err, persescommon.ReasonBackendError)
} else {
_, err = persesClient.Datasource(datasource.Namespace).Update(datasourceWithName)

if err != nil {
dlog.WithError(err).Errorf("Failed to update datasource: %s", datasource.Name)
return subreconciler.RequeueWithErrorAndReason(err, persescommon.ReasonBackendError)
return nil
}
return err
}

dlog.Infof("Datasource updated: %s", datasource.Name)
_, err = persesClient.Datasource(datasource.Namespace).Update(datasourceWithName)
if err != nil {
dlog.WithError(err).Errorf("Failed to update datasource: %s", datasource.Name)
return err
}

res, err := subreconciler.ContinueReconciling()
return res, "", err
dlog.Infof("Datasource updated: %s", datasource.Name)
return nil
}

// creates/updates a Perses Secret with configuration,
Expand Down Expand Up @@ -386,51 +392,60 @@ func (r *PersesDatasourceReconciler) deleteDatasourceInAllInstances(ctx context.
}

func (r *PersesDatasourceReconciler) deleteDatasource(ctx context.Context, perses persesv1alpha2.Perses, datasourceNamespace string, datasourceName string) (*ctrl.Result, error) {
persesClient, err := r.ClientFactory.CreateClient(ctx, r.APIReader, perses)

clients, err := r.ClientFactory.CreateClientsForAllPods(ctx, r.APIReader, perses)
if err != nil {
dlog.WithError(err).Error("Failed to create perses rest client")
dlog.WithError(err).Error("Failed to create perses rest clients")
return subreconciler.RequeueWithError(err)
}

_, err = persesClient.Project().Get(datasourceNamespace)
var errs []error
for _, persesClient := range clients {
if err := r.deleteDatasourceFromClient(persesClient, datasourceNamespace, datasourceName); err != nil {
errs = append(errs, err)
}
}

if len(errs) > 0 {
return subreconciler.RequeueWithError(errors.Join(errs...))
}

return subreconciler.ContinueReconciling()
}

func (r *PersesDatasourceReconciler) deleteDatasourceFromClient(persesClient v1.ClientInterface, datasourceNamespace string, datasourceName string) error {
_, err := persesClient.Project().Get(datasourceNamespace)
if err != nil {
if errors.Is(err, perseshttp.RequestNotFoundError) {
return nil
}
dlog.WithError(err).Errorf("project error: %s", datasourceNamespace)

return subreconciler.RequeueWithError(err)
return err
}

// Ignore NotFound — the resource may have already been deleted from Perses directly.
// Any other error means the delete failed and should be retried.
// Secret delete is attempted regardless of whether the datasource was found or not.
err = persesClient.Datasource(datasourceNamespace).Delete(datasourceName)

if err != nil {
if errors.Is(err, perseshttp.RequestNotFoundError) {
dlog.Infof("Datasource not found: %s", datasourceName)
} else {
dlog.WithError(err).Errorf("Failed to delete datasource: %s", datasourceName)
return subreconciler.RequeueWithError(err)
return err
}
} else {
dlog.Infof("Datasource deleted: %s", datasourceName)
}

secretName := datasourceName + persescommon.SecretNameSuffix

err = persesClient.Secret(datasourceNamespace).Delete(secretName)

if err != nil {
if errors.Is(err, perseshttp.RequestNotFoundError) {
dlog.Infof("Secret not found: %s", secretName)
} else {
dlog.WithError(err).Errorf("Failed to delete secret: %s", secretName)
return subreconciler.RequeueWithError(err)
return err
}
} else {
dlog.Infof("Secret deleted: %s", secretName)
}

return subreconciler.ContinueReconciling()
return nil
}
1 change: 1 addition & 0 deletions controllers/datasources/persesdatasource_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ var log = logger.WithField("module", "perses_datasource_controller")
// +kubebuilder:rbac:groups=perses.dev,resources=persesdatasources/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=perses.dev,resources=persesdatasources/finalizers,verbs=update
// +kubebuilder:rbac:groups="",resources=configmaps;secrets,verbs=watch;get
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch
func (r *PersesDatasourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
start := time.Now()
objKey := req.String()
Expand Down
Loading
Loading