Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
df95743
wip: rewrite reconciliation loop, migrate to go modules and use grafa…
pb82 Nov 10, 2019
ff96602
INTLY-4183-Updating grafana version
R-Lawton Nov 20, 2019
ce79073
travis should work with go modules
pb82 Nov 20, 2019
b1cc302
update go version requirement
pb82 Nov 20, 2019
dfa0309
update modules
pb82 Nov 20, 2019
d8573a2
update Dockerfile, remove templates
pb82 Nov 20, 2019
f52fbc5
basic dashboard api create/update/delete implemented
pb82 Nov 22, 2019
e7f3de0
implement dashboard logic: resync and updates
pb82 Nov 24, 2019
40ff24a
enable metrics
pb82 Nov 24, 2019
b6a092b
cleanup and version update
pb82 Nov 24, 2019
e297fc1
feat: import dashboards after grafana reinstall, improve unavailabili…
pb82 Nov 24, 2019
addeba5
wip - plugin installation
pb82 Nov 24, 2019
8b7bb76
wip: start working on a way to reset dashboards
pb82 Nov 25, 2019
2c1b8da
wip: resilient dashboards (should survive restarts)
pb82 Nov 25, 2019
211d1c2
remove dashboard wait time because it prevents updating plugins
pb82 Nov 25, 2019
f15e6b3
add oauth example
pb82 Nov 25, 2019
820a8d6
fix: persist admin user back to cr to prevent deploymetn from getting…
pb82 Nov 25, 2019
350b9b5
mount extra containers when specified in the cr
pb82 Nov 25, 2019
b480615
fix: missing arg to read grafana deployment
pb82 Nov 25, 2019
3bb2ba8
wip: set default resource requests and limits, hide empty config sect…
pb82 Nov 26, 2019
9acb449
feat: grafana admin credentials from secret
pb82 Nov 26, 2019
63db388
gofmt fixes
pb82 Nov 26, 2019
f45c0f9
graffanaServiceAccount modifications
R-Lawton Nov 28, 2019
f6a2413
update autogenerated code
pb82 Nov 28, 2019
b65d461
add datasources
StevenTobin Nov 29, 2019
2d25d10
delete datasources
pb82 Nov 29, 2019
fa462fd
adding permissions
R-Lawton Nov 29, 2019
275331e
add grafanadatasourceconfig to model
StevenTobin Nov 29, 2019
de46e59
mount datasources volume to provisioning directory
StevenTobin Nov 29, 2019
6789289
fix codee checks
StevenTobin Nov 29, 2019
08c705a
fixing forrmatting error
R-Lawton Nov 29, 2019
44302f9
comment changes and hardcode provisioning path
StevenTobin Nov 29, 2019
cae6afe
code check fixes
StevenTobin Nov 29, 2019
9cad949
updates to ensure new reconciler and grafana api work on vanilla kube
pb82 Nov 30, 2019
d33fc49
update permissions
pb82 Nov 30, 2019
8677d17
restart grafana on datasource change, do not reconcile datasources wh…
pb82 Dec 1, 2019
f63d7a6
implement datasource hashing
pb82 Dec 1, 2019
e711e83
improve datasource update handling
pb82 Dec 1, 2019
8b80abc
allow configuration of service ports and route target ports
pb82 Dec 1, 2019
908aede
wip: ensure config defaults
pb82 Dec 1, 2019
4a83a2e
wip: rework ini configuration
pb82 Dec 1, 2019
b2a6143
add client configuration options
pb82 Dec 1, 2019
318f7cd
update examples, only update datasource status if they have changed
pb82 Dec 1, 2019
ce9f3c2
rewrite ini config system
pb82 Dec 1, 2019
a757d2a
update oauth example
pb82 Dec 1, 2019
8d799f2
fix: service ports did not return existing
pb82 Dec 1, 2019
a5724f4
remove unused code
pb82 Dec 1, 2019
021347a
remove ini dependency
pb82 Dec 2, 2019
95fa747
add backwards compatibility options
pb82 Dec 2, 2019
74437bc
fix: correctly identify dashboards when scan all is enabled
pb82 Dec 2, 2019
63577da
update documentation
pb82 Dec 5, 2019
aac4c49
add changelog
pb82 Dec 5, 2019
df00168
add a note about the Grafana version
pb82 Dec 6, 2019
dee56ca
bump version to 3.0.0
pb82 Dec 6, 2019
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
Prev Previous commit
Next Next commit
add backwards compatibility options
  • Loading branch information
pb82 committed Dec 2, 2019
commit 95fa7478f70d3a463d770984f2ab18878558862c
3 changes: 3 additions & 0 deletions deploy/crds/Grafana.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ spec:
client:
type: object
description: Grafana client settings
compat:
type: object
description: Backwards compatibility switches
dashboardLabelSelectors:
type: array
items:
Expand Down
7 changes: 7 additions & 0 deletions pkg/apis/integreatly/v1alpha1/grafana_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,15 @@ type GrafanaSpec struct {
Resources *v1.ResourceRequirements `json:"resources,omitempty"`
ServiceAccount *GrafanaServiceAccount `json:"serviceAccount,omitempty"`
Client *GrafanaClient `json:"client,omitempty"`
Compat *GrafanaCompat `json:"compat"`
}

// Backwards compatibility switches
type GrafanaCompat struct {
FixAnnotations bool `json:"fixAnnotations"`
}

// Grafana API client settings
type GrafanaClient struct {
TimeoutSeconds *int `json:"timeout"`
PreferService bool `json:"preferService"`
Expand Down
21 changes: 21 additions & 0 deletions pkg/apis/integreatly/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/controller/common/controllerState.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ type ControllerState struct {
AdminUrl string
GrafanaReady bool
ClientTimeout int
FixAnnotations bool
}
10 changes: 1 addition & 9 deletions pkg/controller/config/controller_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,17 @@ const (
ConfigGrafanaImageTag = "grafana.image.tag"
ConfigPluginsInitContainerImage = "grafana.plugins.init.container.image.url"
ConfigPluginsInitContainerTag = "grafana.plugins.init.container.image.tag"
ConfigPodLabelValue = "grafana.pod.label"
ConfigOperatorNamespace = "grafana.operator.namespace"
ConfigDashboardLabelSelector = "grafana.dashboard.selector"
ConfigOpenshift = "mode.openshift"
GrafanaConfigMapName = "grafana-config"
GrafanaConfigFileName = "grafana.ini"
GrafanaDashboardsConfigMapName = "grafana-dashboards"
GrafanaDeploymentName = "grafana-deployment"
GrafanaDataPath = "/var/lib/grafana"
GrafanaLogsPath = "/var/log/grafana"
GrafanaPluginsPath = "/var/lib/grafana/plugins"
GrafanaProvisioningPath = "/etc/grafana/provisioning/"
PluginsInitContainerImage = "quay.io/integreatly/grafana_plugins_init"
PluginsInitContainerTag = "0.0.2"
PluginsEnvVar = "GRAFANA_PLUGINS"
PluginsUrl = "https://grafana.com/api/plugins/%s/versions/%s"
InitContainerName = "grafana-plugins-init"
RequeueDelay = time.Second * 5
PodLabelDefaultValue = "grafana"
RequeueDelay = time.Second * 10
SecretsMountDir = "/etc/grafana-secrets/"
ConfigMapsMountDir = "/etc/grafana-configmaps/"
ConfigRouteWatch = "watch.routes"
Expand Down
11 changes: 11 additions & 0 deletions pkg/controller/grafana/grafana_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ func (r *ReconcileGrafana) manageSuccess(cr *i8ly.Grafana, state *common.Cluster
}
}

if state.AdminSecret == nil || state.AdminSecret.Data == nil {
return r.manageError(cr, stdErr.New("admin secret not found or invalud"))
}

err := r.client.Status().Update(r.context, cr)
if err != nil {
return r.manageError(cr, err)
Expand All @@ -262,6 +266,12 @@ func (r *ReconcileGrafana) manageSuccess(cr *i8ly.Grafana, state *common.Cluster
return r.manageError(cr, err)
}

// Try to fix annotations on older dashboards?
fixAnnotations := false
if cr.Spec.Compat != nil && cr.Spec.Compat.FixAnnotations {
fixAnnotations = true
}

// Publish controller state
controllerState := common.ControllerState{
DashboardSelectors: cr.Spec.DashboardLabelSelector,
Expand All @@ -270,6 +280,7 @@ func (r *ReconcileGrafana) manageSuccess(cr *i8ly.Grafana, state *common.Cluster
AdminUrl: url,
GrafanaReady: true,
ClientTimeout: DefaultClientTimeoutSeconds,
FixAnnotations: fixAnnotations,
}

if cr.Spec.Client != nil && cr.Spec.Client.TimeoutSeconds != nil {
Expand Down
9 changes: 7 additions & 2 deletions pkg/controller/grafanadashboard/dashboard_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ func (r *ReconcileGrafanaDashboard) Reconcile(request reconcile.Request) (reconc
// If the dashboard does not match the label selectors then we ignore it
cr := instance.DeepCopy()
if !r.isMatch(cr) {
log.Info(fmt.Sprintf("dashboard %v/%v found but selectors do not match",
cr.Namespace, cr.Name))
return reconcile.Result{}, nil
}

Expand Down Expand Up @@ -198,28 +200,31 @@ func (r *ReconcileGrafanaDashboard) reconcileDashboards(request reconcile.Reques
for _, dashboard := range namespaceDashboards.Items {
// Is this a dashboard we care about (matches the label selectors)?
if !r.isMatch(&dashboard) {
log.Info(fmt.Sprintf("dashboard %v/%v found but selectors do not match",
dashboard.Namespace, dashboard.Name))
continue
}

// Process the dashboard. Use the known hash of an existing dashboard
// to determine if an update is required
knownHash := findHash(&dashboard)
pipeline := NewDashboardPipeline(&dashboard)
pipeline := NewDashboardPipeline(&dashboard, r.state.FixAnnotations)
processed, err := pipeline.ProcessDashboard(knownHash)

if err != nil {
log.Info(fmt.Sprintf("cannot process dashboard %v/%v", dashboard.Namespace, dashboard.Name))
r.manageError(&dashboard, err)
continue
}

if processed == nil {
log.Info(fmt.Sprintf("dashboard %v unchanged", dashboard.Name))
r.config.SetPluginsFor(&dashboard)
continue
}

status, err := grafanaClient.CreateOrUpdateDashboard(*processed)
if err != nil {
log.Info(fmt.Sprintf("cannot submit dashboard %v/%v", dashboard.Namespace, dashboard.Name))
r.manageError(&dashboard, err)
continue
}
Expand Down
73 changes: 58 additions & 15 deletions pkg/controller/grafanadashboard/dashboard_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,20 @@ type DashboardPipeline interface {
}

type DashboardPipelineImpl struct {
Dashboard *v1alpha1.GrafanaDashboard
JSON string
Board sdk.Board
Logger logr.Logger
Hash string
Dashboard *v1alpha1.GrafanaDashboard
JSON string
Board sdk.Board
Logger logr.Logger
Hash string
FixAnnotations bool
}

func NewDashboardPipeline(dashboard *v1alpha1.GrafanaDashboard) DashboardPipeline {
func NewDashboardPipeline(dashboard *v1alpha1.GrafanaDashboard, fixAnnotations bool) DashboardPipeline {
return &DashboardPipelineImpl{
Dashboard: dashboard,
JSON: "",
Logger: logf.Log.WithName(fmt.Sprintf("dashboard-%v", dashboard.Name)),
Dashboard: dashboard,
JSON: "",
Logger: logf.Log.WithName(fmt.Sprintf("dashboard-%v", dashboard.Name)),
FixAnnotations: fixAnnotations,
}
}

Expand All @@ -55,6 +57,10 @@ func (r *DashboardPipelineImpl) ProcessDashboard(knownHash string) (*sdk.Board,
return nil, err
}

// Dashboards are never expected to come with an ID, it is
// always assigned by Grafana. If there is one, we ignore it
r.Board.ID = 0

// This dashboard has previously been imported
// To make sure its updated we have to set the metadata
if r.Dashboard.Status.Phase == v1alpha1.PhaseReconciling {
Expand All @@ -68,9 +74,14 @@ func (r *DashboardPipelineImpl) ProcessDashboard(knownHash string) (*sdk.Board,

// Make sure the dashboard contains valid JSON
func (r *DashboardPipelineImpl) validateJson() error {
log.Info("validating dashboard contents")
dashboardBytes := []byte(r.JSON)
dashboardBytes, err := r.fixAnnotations(dashboardBytes)
if err != nil {
return err
}

var dashboard sdk.Board
err := json.Unmarshal([]byte(r.JSON), &dashboard)
err = json.Unmarshal(dashboardBytes, &dashboard)
if err != nil {
return err
}
Expand All @@ -82,8 +93,6 @@ func (r *DashboardPipelineImpl) validateJson() error {
// Try to get the dashboard json definition either from a provided URL or from the
// raw json in the dashboard resource
func (r *DashboardPipelineImpl) obtainJson() error {
r.Logger.Info("obtaining dashboard json")

if r.Dashboard.Spec.Url != "" {
err := r.loadDashboardFromURL()
if err != nil {
Expand All @@ -105,13 +114,11 @@ func (r *DashboardPipelineImpl) obtainJson() error {
// If there are no changes we should avoid sending update requests as this will create
// a new dashboard version in Grafana
func (r *DashboardPipelineImpl) generateHash() string {
r.Logger.Info("generating dashboard hash")
return fmt.Sprintf("%x", md5.Sum([]byte(r.Dashboard.Spec.Json+r.Dashboard.Spec.Url)))
}

// Try to obtain the dashboard json from a provided url
func (r *DashboardPipelineImpl) loadDashboardFromURL() error {
r.Logger.Info("loading dashboard from url")
_, err := url.ParseRequestURI(r.Dashboard.Spec.Url)
if err != nil {
return errors.New(fmt.Sprintf("invalid url %v", r.Dashboard.Spec.Url))
Expand All @@ -136,3 +143,39 @@ func (r *DashboardPipelineImpl) loadDashboardFromURL() error {
func (r *DashboardPipelineImpl) NewHash() string {
return r.Hash
}

// Some older dashboards provide the tags list of an annotation as an array
// instead of a string
func (r *DashboardPipelineImpl) fixAnnotations(dashboardBytes []byte) ([]byte, error) {
if !r.FixAnnotations {
return dashboardBytes, nil
}

raw := map[string]interface{}{}
err := json.Unmarshal(dashboardBytes, &raw)
if err != nil {
return nil, err
}

if raw != nil && raw["annotations"] != nil {
annotations := raw["annotations"].(map[string]interface{})
if annotations != nil && annotations["list"] != nil {
annotationsList := annotations["list"].([]interface{})
for _, annotation := range annotationsList {
rawAnnotation := annotation.(map[string]interface{})
if rawAnnotation["tags"] != nil {
// Don't attempty to convert the tags, just replace them
// with something that is compatible
rawAnnotation["tags"] = ""
}
}
}
}

dashboardBytes, err = json.Marshal(raw)
if err != nil {
return nil, err
}

return dashboardBytes, nil
}
2 changes: 1 addition & 1 deletion pkg/controller/model/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const (
LastConfigAnnotation = "last-config"
LastConfigEnvVar = "LAST_CONFIG"
LastDatasourcesConfigEnvVar = "LAST_DATASOURCES"
GrafanaAdminSecretName = "admin-credentials"
GrafanaAdminSecretName = "grafana-admin-credentials"
DefaultAdminUser = "admin"
GrafanaAdminUserEnvVar = "GF_SECURITY_ADMIN_USER"
GrafanaAdminPasswordEnvVar = "GF_SECURITY_ADMIN_PASSWORD"
Expand Down