Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
pb82 committed Apr 16, 2019
1 parent 5a52cd0 commit d465b99
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 67 deletions.
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
ORG=integreatly
ORG=pb82
NAMESPACE=application-monitoring
PROJECT=grafana-operator
REG=quay.io
REG=docker.io
SHELL=/bin/bash
TAG=0.0.1
TAG=latest
PKG=github.com/integr8ly/grafana-operator
TEST_DIRS?=$(shell sh -c "find $(TOP_SRC_DIRS) -name \\*_test.go -exec dirname {} \\; | sort | uniq")
TEST_POD_NAME=grafana-operator-test
Expand Down
14 changes: 14 additions & 0 deletions deploy/crds/GrafanaDashboard.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,17 @@ spec:
singular: grafanadashboard
scope: Namespaced
version: v1alpha1
validation:
openAPIV3Schema:
properties:
spec:
properties:
name:
type: string
json:
type: string
plugins:
type: array
items:
description: Grafana Plugin Object
type: object
11 changes: 8 additions & 3 deletions deploy/examples/GrafanaDashboard.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
apiVersion: integreatly.org/v1alpha1
kind: GrafanaDashboard
metadata:
name: example-grafanadashboard
name: example
spec:
# Add fields here
size: 3
name: dashboard.json
json: ""
plugins:
- name: "grafana-piechart-panel"
version: "1.3.6"
- name: "grafana-clock-panel"
version: "1.0.2"
12 changes: 7 additions & 5 deletions pkg/apis/integreatly/v1alpha1/grafana_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ type GrafanaStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
Phase int `json:"phase"`
InstalledPlugins []GrafanaPlugin `json:"installedPlugins,omitempty"`
InstalledPlugins []GrafanaPlugin `json:"installedPlugins"`
}

// GrafanaPlugin contains information about a single plugin
type GrafanaPlugin struct {
Name string `json:"name"`
Version string `json:"version"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand All @@ -44,10 +50,6 @@ type GrafanaList struct {
Items []Grafana `json:"items"`
}

type GrafanaPlugin struct {
Name string
Version string
}

func init() {
SchemeBuilder.Register(&Grafana{}, &GrafanaList{})
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/integreatly/v1alpha1/grafanadashboard_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type GrafanaDashboardSpec struct {
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
Json string `json:"json"`
Name string `json:"name"`
Plugins []GrafanaPlugin `json:"plugins,omitempty"`
Plugins []GrafanaPlugin `json:"plugins"`
}

// GrafanaDashboardStatus defines the observed state of GrafanaDashboard
Expand Down
32 changes: 32 additions & 0 deletions pkg/apis/integreatly/v1alpha1/zz_generated.defaults.go

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

89 changes: 52 additions & 37 deletions pkg/controller/grafana/grafana_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ type ReconcileGrafana struct {
// The Controller will requeue the Request to be processed again if the returned error is non-nil or
// Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
func (r *ReconcileGrafana) Reconcile(request reconcile.Request) (reconcile.Result, error) {
reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
reqLogger.Info("Reconciling Grafana")
// reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
// reqLogger.Info("Reconciling Grafana")

// Fetch the Grafana instance
instance := &integreatly.Grafana{}
Expand Down Expand Up @@ -136,70 +136,85 @@ func (r *ReconcileGrafana) ReconcileNamespaces(cr *integreatly.Grafana) (reconci
}

if len(namespaces) >= 1 {
var requestedPlugins []integreatly.GrafanaPlugin

for _, ns := range namespaces {
log.Info(fmt.Sprintf("Checking namespace %s for dashboards", ns.Name))
dashboards, err := r.helper.getNamespaceDashboards(ns.Name)

if err != nil {
log.Error(err, "Error listing dashboards in namespace")
return reconcile.Result{}, err
} else {
if len(dashboards.Items) >= 1 {
for _, d := range dashboards.Items {
r.ReconcileDashboards(cr, d)
dashboardCopy := d.DeepCopy()
r.ReconcileDashboards(cr, dashboardCopy)
newPlugins := r.CollectPluginsFromDashboards(dashboardCopy, requestedPlugins)
requestedPlugins = append(requestedPlugins, newPlugins...)
}
}
}
}
} else {
log.Info("No monitoring namespaces, nothing to do")

actionablePlugins := r.GetActionablePlugins(cr, requestedPlugins)
log.Info(fmt.Sprintf("Requested plugins: %s", requestedPlugins))
log.Info(fmt.Sprintf("Actionable plugins: %s", actionablePlugins))
if len(actionablePlugins) > 0 {
r.ReconcilePlugins(cr, actionablePlugins)
}
}

return reconcile.Result{RequeueAfter: time.Second * 10}, nil
}

func (r *ReconcileGrafana) ReconcileDashboards(cr *integreatly.Grafana, d integreatly.GrafanaDashboard) {
log.Info(fmt.Sprintf("Reconciling dashboard: %s", d.Name))
err := r.helper.updateDashboard(cr.Namespace, d.Namespace, &d)
func (r *ReconcileGrafana) ReconcileDashboards(cr *integreatly.Grafana, d *integreatly.GrafanaDashboard) {
err := r.helper.updateDashboard(cr.Namespace, d.Namespace, d)
if err != nil {
log.Error(err, "Error updating dashboard config")
}
}

err = r.ReconcilePlugins(cr, d)
if err != nil {
log.Error(err, "Error reconciling grafana plugins")
func (r *ReconcileGrafana) GetActionablePlugins(cr *integreatly.Grafana, plugins []integreatly.GrafanaPlugin) []integreatly.GrafanaPlugin {
var list []integreatly.GrafanaPlugin
for _, plugin := range plugins {
pluginStatus := r.plugins.pluginInstalled(plugin, cr.Status.InstalledPlugins)
if pluginStatus == PluginNotInstalled || pluginStatus == PluginInstalledVersionDifferent {
list = append(list, plugin)
}
}
return list
}

func (r *ReconcileGrafana) ReconcilePlugins(cr *integreatly.Grafana, d integreatly.GrafanaDashboard) error {
if len(d.Spec.Plugins) > 0 {
pluginsAdded := false
for _, plugin := range d.Spec.Plugins {
log.Info(fmt.Sprintf("Processing requested plugin %s", plugin.Name))
if r.plugins.pluginInstalled(plugin, cr) {
log.Info(fmt.Sprintf("%s is already installed", plugin.Name))
continue
}
func (r *ReconcileGrafana) CollectPluginsFromDashboards(d *integreatly.GrafanaDashboard, plugins []integreatly.GrafanaPlugin) []integreatly.GrafanaPlugin {
var list []integreatly.GrafanaPlugin
for _, plugin := range d.Spec.Plugins {
if r.plugins.pluginInstalled(plugin, plugins) == PluginNotInstalled {
list = append(list, plugin)
}
}
return list
}

if r.plugins.pluginExists(plugin) == false {
log.Info(fmt.Sprintf("Unknown plugin %s", plugin.Name))
continue
}
func (r *ReconcileGrafana) ReconcilePlugins(cr *integreatly.Grafana, plugins []integreatly.GrafanaPlugin) error {
var validPlugins []integreatly.GrafanaPlugin

log.Info("Adding new plugin %s", plugin.Name)
cr.Status.InstalledPlugins = append(cr.Status.InstalledPlugins, plugin)
pluginsAdded = true
for _, plugin := range plugins {
if r.plugins.pluginExists(plugin) == false {
continue
}

if pluginsAdded {
err := r.client.Update(context.TODO(), cr)
if err != nil {
return err
}
log.Info(fmt.Sprintf("Installing plugin: %s@%s", plugin.Name, plugin.Version))
validPlugins = append(validPlugins, plugin)
}

newEnv := r.plugins.buildEnv(cr)
err = r.helper.updateGrafanaDeployment(cr.Namespace, newEnv)
if len(validPlugins) > 0 {
cr.Status.InstalledPlugins = validPlugins
err := r.client.Update(context.TODO(), cr)
if err != nil {
return err
}

newEnv := r.plugins.buildEnv(cr)
err = r.helper.updateGrafanaDeployment(cr.Namespace, newEnv)
return nil
}

return nil
Expand Down
23 changes: 14 additions & 9 deletions pkg/controller/grafana/kubeHelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/config"
)

const (
PluginInstalled = iota
PluginNotInstalled
PluginInstalledVersionDifferent
)

type KubeHelperImpl struct {
k8client *kubernetes.Clientset
grclient *gr.Clientset
Expand Down Expand Up @@ -75,12 +81,6 @@ func (h KubeHelperImpl) updateDashboard(monitoringNamespace string, dashboardNam

configMap.Data[dashboard.Spec.Name] = dashboard.Spec.Json
_, err = h.k8client.CoreV1().ConfigMaps(monitoringNamespace).Update(configMap)

if err == nil {
dashboard.Status.Created = true
_, err = h.grclient.IntegreatlyV1alpha1().GrafanaDashboards(dashboardNamespace).Update(dashboard)
}

return err
}

Expand All @@ -95,12 +95,17 @@ func (h KubeHelperImpl) updateGrafanaDeployment(monitoringNamespace string, newE
return err
}

// Leave the deployment alone when it's busy with another operation
if deployment.Status.Replicas != deployment.Status.ReadyReplicas {
return nil
}

updated := false
for _, container := range deployment.Spec.Template.Spec.InitContainers {
for i, container := range deployment.Spec.Template.Spec.InitContainers {
if container.Name == "grafana-plugins-init" {
for _, env := range container.Env {
for j, env := range deployment.Spec.Template.Spec.InitContainers[i].Env {
if env.Name == PluginsEnvVar {
env.Value = newEnv
deployment.Spec.Template.Spec.InitContainers[i].Env[j].Value = newEnv
updated = true
break
}
Expand Down
17 changes: 9 additions & 8 deletions pkg/controller/grafana/pluginsHelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,14 @@ func (h *PluginsHelperImpl) buildEnv(cr *integreatly.Grafana) string {
return strings.Join(env, ",")
}

// Checks if a given plugin is already installed. We do not allow to install
// multiple versions of the same plugin
func (h *PluginsHelperImpl) pluginInstalled(plugin integreatly.GrafanaPlugin, cr *integreatly.Grafana) bool {
for _, installedPlugin := range cr.Status.InstalledPlugins {
if installedPlugin.Name == plugin.Name {
return true
// Checks if a given plugin is a member of a list of plugins
func (h *PluginsHelperImpl) pluginInstalled(plugin integreatly.GrafanaPlugin, plugins []integreatly.GrafanaPlugin) int {
for _, listed := range plugins {
if listed.Name == plugin.Name && listed.Version == plugin.Version {
return PluginInstalled
} else if listed.Name == plugin.Name && listed.Version != plugin.Version {
return PluginInstalledVersionDifferent
}
}
return false
}
return PluginNotInstalled
}
2 changes: 1 addition & 1 deletion templates/grafana-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ spec:
- env:
- name: GRAFANA_PLUGINS
value: ""
image: 'quay.io/integreatly/grafana_plugins_init:0.0.1'
image: 'docker.io/pb82/grafana_plugins_init:latest'
imagePullPolicy: IfNotPresent
name: grafana-plugins-init
resources: {}
Expand Down

0 comments on commit d465b99

Please sign in to comment.