Skip to content

chore(grafana controller): Merge startup sync #2013

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
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
6 changes: 3 additions & 3 deletions api/v1beta1/grafanacontactpoint_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ type GrafanaContactPointList struct {
Items []GrafanaContactPoint `json:"items"`
}

func (in *GrafanaContactPointList) Find(namespace string, name string) *GrafanaContactPoint {
func (in *GrafanaContactPointList) Exists(namespace, name string) bool {
for _, contactpoint := range in.Items {
if contactpoint.Namespace == namespace && contactpoint.Name == name {
return &contactpoint
return true
}
}
return nil
return false
}

// Wrapper around CustomUID or default metadata.uid
Expand Down
6 changes: 3 additions & 3 deletions api/v1beta1/grafanadashboard_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,13 @@ func (in *GrafanaDashboard) GrafanaContentStatus() *GrafanaContentStatus {

var _ GrafanaContentResource = &GrafanaDashboard{}

func (in *GrafanaDashboardList) Find(namespace string, name string) *GrafanaDashboard {
func (in *GrafanaDashboardList) Exists(namespace, name string) bool {
for _, dashboard := range in.Items {
if dashboard.Namespace == namespace && dashboard.Name == name {
return &dashboard
return true
}
}
return nil
return false
}

func (in *GrafanaDashboard) MatchLabels() *metav1.LabelSelector {
Expand Down
6 changes: 3 additions & 3 deletions api/v1beta1/grafanadatasource_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,13 @@ func (in *GrafanaDatasource) CustomUIDOrUID() string {
return string(in.UID)
}

func (in *GrafanaDatasourceList) Find(namespace string, name string) *GrafanaDatasource {
func (in *GrafanaDatasourceList) Exists(namespace, name string) bool {
for _, datasource := range in.Items {
if datasource.Namespace == namespace && datasource.Name == name {
return &datasource
return true
}
}
return nil
return false
}

func (in *GrafanaDatasource) MatchLabels() *metav1.LabelSelector {
Expand Down
6 changes: 3 additions & 3 deletions api/v1beta1/grafanafolder_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,13 @@ func init() {
SchemeBuilder.Register(&GrafanaFolder{}, &GrafanaFolderList{})
}

func (in *GrafanaFolderList) Find(namespace string, name string) *GrafanaFolder {
func (in *GrafanaFolderList) Exists(namespace, name string) bool {
for _, folder := range in.Items {
if folder.Namespace == namespace && folder.Name == name {
return &folder
return true
}
}
return nil
return false
}

func (in *GrafanaFolder) Hash() string {
Expand Down
6 changes: 3 additions & 3 deletions api/v1beta1/grafanalibrarypanel_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,13 @@ func (in *GrafanaLibraryPanel) GrafanaContentStatus() *GrafanaContentStatus {

var _ GrafanaContentResource = &GrafanaLibraryPanel{}

func (in *GrafanaLibraryPanelList) Find(namespace string, name string) *GrafanaLibraryPanel {
func (in *GrafanaLibraryPanelList) Exists(namespace, name string) bool {
for _, e := range in.Items {
if e.Namespace == namespace && e.Name == name {
return &e
return true
}
}
return nil
return false
}

func init() {
Expand Down
5 changes: 5 additions & 0 deletions api/v1beta1/namespaced_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ type NamespacedResource string

type NamespacedResourceList []NamespacedResource

// +kubebuilder:object:generate=false
type NamespacedResourceImpl interface {
Exists(namespace string, name string) bool
}

func (in NamespacedResource) Split() (string, string, string) {
parts := strings.Split(string(in), "/")
return parts[0], parts[1], parts[2]
Expand Down
82 changes: 2 additions & 80 deletions controllers/contactpoint_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"encoding/json"
"fmt"
"strings"
"time"

kuberr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
Expand All @@ -37,7 +36,6 @@ import (
"github.com/grafana/grafana-openapi-client-go/models"
grafanav1beta1 "github.com/grafana/grafana-operator/v5/api/v1beta1"
client2 "github.com/grafana/grafana-operator/v5/controllers/client"
"github.com/grafana/grafana-operator/v5/controllers/metrics"
)

const (
Expand All @@ -54,55 +52,6 @@ type GrafanaContactPointReconciler struct {
//+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanacontactpoints/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanacontactpoints/finalizers,verbs=update

func (r *GrafanaContactPointReconciler) syncStatuses(ctx context.Context) error {
log := logf.FromContext(ctx)

// get all grafana instances
grafanas := &grafanav1beta1.GrafanaList{}
var opts []client.ListOption
err := r.List(ctx, grafanas, opts...)
if err != nil {
return err
}
// no instances, no need to sync
if len(grafanas.Items) == 0 {
return nil
}

// get all contact points
allContactPoints := &grafanav1beta1.GrafanaContactPointList{}
err = r.List(ctx, allContactPoints, opts...)
if err != nil {
return err
}

// delete contact points from grafana statuses that do no longer have a cr
contactpointsSynced := 0
for _, grafana := range grafanas.Items {
statusUpdated := false
for _, contactpoint := range grafana.Status.ContactPoints {
namespace, name, _ := contactpoint.Split()
if allContactPoints.Find(namespace, name) == nil {
grafana.Status.ContactPoints = grafana.Status.ContactPoints.Remove(namespace, name)
contactpointsSynced += 1
statusUpdated = true
}
}

if statusUpdated {
err = r.Client.Status().Update(ctx, &grafana)
if err != nil {
return err
}
}
}

if contactpointsSynced > 0 {
log.Info("successfully synced contact points", "contactpoints", contactpointsSynced)
}
return nil
}

func (r *GrafanaContactPointReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := logf.FromContext(ctx).WithName("GrafanaContactPointReconciler")
ctx = logf.IntoContext(ctx, log)
Expand Down Expand Up @@ -287,36 +236,9 @@ func (r *GrafanaContactPointReconciler) finalize(ctx context.Context, contactPoi
}

// SetupWithManager sets up the controller with the Manager.
func (r *GrafanaContactPointReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
err := ctrl.NewControllerManagedBy(mgr).
func (r *GrafanaContactPointReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&grafanav1beta1.GrafanaContactPoint{}).
WithEventFilter(ignoreStatusUpdates()).
Complete(r)
if err != nil {
return err
}

go func() {
log := logf.FromContext(ctx).WithName("GrafanaContactPointReconciler")
for {
select {
case <-ctx.Done():
return
case <-time.After(initialSyncDelay):
start := time.Now()
err := r.syncStatuses(ctx)
elapsed := time.Since(start).Milliseconds()
metrics.InitialContactPointSyncDuration.Set(float64(elapsed))
if err != nil {
log.Error(err, "error synchronizing contact points")
continue
}

log.Info("contact point sync complete")
return
}
}
}()

return nil
}
80 changes: 1 addition & 79 deletions controllers/dashboard_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"net/http"
"reflect"
"strings"
"time"

"k8s.io/utils/strings/slices"

Expand All @@ -35,7 +34,6 @@ import (
"github.com/grafana/grafana-operator/v5/api/v1beta1"
client2 "github.com/grafana/grafana-operator/v5/controllers/client"
"github.com/grafana/grafana-operator/v5/controllers/content"
"github.com/grafana/grafana-operator/v5/controllers/metrics"
corev1 "k8s.io/api/core/v1"
kuberr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
Expand Down Expand Up @@ -65,55 +63,6 @@ type GrafanaDashboardReconciler struct {
//+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanadashboards/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanadashboards/finalizers,verbs=update

func (r *GrafanaDashboardReconciler) syncStatuses(ctx context.Context) error {
log := logf.FromContext(ctx)

// get all grafana instances
grafanas := &v1beta1.GrafanaList{}
var opts []client.ListOption
err := r.List(ctx, grafanas, opts...)
if err != nil {
return err
}
// no instances, no need to sync
if len(grafanas.Items) == 0 {
return nil
}

// get all dashboards
allDashboards := &v1beta1.GrafanaDashboardList{}
err = r.List(ctx, allDashboards, opts...)
if err != nil {
return err
}

// delete dashboards from grafana statuses that do no longer have a cr
dashboardsSynced := 0
for _, grafana := range grafanas.Items {
statusUpdated := false
for _, dashboard := range grafana.Status.Dashboards {
namespace, name, _ := dashboard.Split()
if allDashboards.Find(namespace, name) == nil {
grafana.Status.Dashboards = grafana.Status.Dashboards.Remove(namespace, name)
dashboardsSynced += 1
statusUpdated = true
}
}

if statusUpdated {
err = r.Client.Status().Update(ctx, &grafana)
if err != nil {
return err
}
}
}

if dashboardsSynced > 0 {
log.Info("successfully synced dashboards", "dashboards", dashboardsSynced)
}
return nil
}

func (r *GrafanaDashboardReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { //nolint:gocyclo
log := logf.FromContext(ctx).WithName("GrafanaDashboardReconciler")
ctx = logf.IntoContext(ctx, log)
Expand Down Expand Up @@ -573,7 +522,7 @@ func (r *GrafanaDashboardReconciler) SetupWithManager(ctx context.Context, mgr c
return fmt.Errorf("failed setting index fields: %w", err)
}

err := ctrl.NewControllerManagedBy(mgr).
return ctrl.NewControllerManagedBy(mgr).
For(&v1beta1.GrafanaDashboard{}, builder.WithPredicates(
ignoreStatusUpdates(),
)).
Expand All @@ -582,33 +531,6 @@ func (r *GrafanaDashboardReconciler) SetupWithManager(ctx context.Context, mgr c
handler.EnqueueRequestsFromMapFunc(r.requestsForChangeByField(configMapIndexKey)),
).
Complete(r)
if err != nil {
return err
}

go func() {
log := logf.FromContext(ctx).WithName("GrafanaDashboardReconciler")
for {
select {
case <-ctx.Done():
return
case <-time.After(initialSyncDelay):
start := time.Now()
err := r.syncStatuses(ctx)
elapsed := time.Since(start).Milliseconds()
metrics.InitialDashboardSyncDuration.Set(float64(elapsed))
if err != nil {
log.Error(err, "error synchronizing dashboards")
continue
}

log.Info("dashboard sync complete")
return
}
}
}()

return nil
}

func (r *GrafanaDashboardReconciler) indexConfigMapSource() func(o client.Object) []string {
Expand Down
Loading