├── .chainsaw.yaml
├── .dockerignore
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── dependabot.yml
├── labeler.yml
├── release.yml
└── workflows
│ ├── e2e.yaml
│ ├── hugo.yaml
│ ├── labeler.yaml
│ ├── pr-hugo.yaml
│ ├── pr-validation.yaml
│ ├── release.yaml
│ └── stale.yml
├── .gitignore
├── .golangci.yaml
├── .ko.yaml
├── .pre-commit-config.yaml
├── CODEOWNERS
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── PREPARE_RELEASE.md
├── PROJECT
├── README.md
├── Toolchain.mk
├── api
├── folderreferencer.go
└── v1beta1
│ ├── common.go
│ ├── common_test.go
│ ├── content.go
│ ├── grafana_types.go
│ ├── grafanaalertrulegroup_types.go
│ ├── grafanaalertrulegroup_types_test.go
│ ├── grafanacontactpoint_types.go
│ ├── grafanacontactpoint_types_test.go
│ ├── grafanadashboard_types.go
│ ├── grafanadashboard_types_test.go
│ ├── grafanadatasource_types.go
│ ├── grafanadatasource_types_test.go
│ ├── grafanafolder_types.go
│ ├── grafanafolder_types_test.go
│ ├── grafanalibrarypanel_types.go
│ ├── grafanalibrarypanel_types_test.go
│ ├── grafanamutetiming_types.go
│ ├── grafanamutetiming_types_test.go
│ ├── grafananotificationpolicy_types.go
│ ├── grafananotificationpolicy_types_test.go
│ ├── grafananotificationpolicyroute_types.go
│ ├── grafananotificationtemplate_types.go
│ ├── grafananotificationtemplate_types_test.go
│ ├── groupversion_info.go
│ ├── namespaced_resource.go
│ ├── plugin_list.go
│ ├── plugin_list_test.go
│ ├── suite_test.go
│ ├── typeoverrides.go
│ └── zz_generated.deepcopy.go
├── bundle.Dockerfile
├── bundle
├── manifests
│ ├── grafana-operator-manager-config_v1_configmap.yaml
│ ├── grafana-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml
│ ├── grafana-operator-operator-metrics-service_v1_service.yaml
│ ├── grafana-operator.clusterserviceversion.yaml
│ ├── grafana.integreatly.org_grafanaalertrulegroups.yaml
│ ├── grafana.integreatly.org_grafanacontactpoints.yaml
│ ├── grafana.integreatly.org_grafanadashboards.yaml
│ ├── grafana.integreatly.org_grafanadatasources.yaml
│ ├── grafana.integreatly.org_grafanafolders.yaml
│ └── grafana.integreatly.org_grafanas.yaml
└── metadata
│ └── annotations.yaml
├── catalog-info.yaml
├── config
├── crd
│ ├── bases
│ │ ├── grafana.integreatly.org_grafanaalertrulegroups.yaml
│ │ ├── grafana.integreatly.org_grafanacontactpoints.yaml
│ │ ├── grafana.integreatly.org_grafanadashboards.yaml
│ │ ├── grafana.integreatly.org_grafanadatasources.yaml
│ │ ├── grafana.integreatly.org_grafanafolders.yaml
│ │ ├── grafana.integreatly.org_grafanalibrarypanels.yaml
│ │ ├── grafana.integreatly.org_grafanamutetimings.yaml
│ │ ├── grafana.integreatly.org_grafananotificationpolicies.yaml
│ │ ├── grafana.integreatly.org_grafananotificationpolicyroutes.yaml
│ │ ├── grafana.integreatly.org_grafananotificationtemplates.yaml
│ │ └── grafana.integreatly.org_grafanas.yaml
│ ├── kustomization.yaml
│ ├── kustomizeconfig.yaml
│ └── patches
│ │ ├── cainjection_in_grafanaalertrulegroups.yaml
│ │ ├── cainjection_in_grafanacontactpoints.yaml
│ │ ├── cainjection_in_grafanadashboards.yaml
│ │ ├── cainjection_in_grafanadatasources.yaml
│ │ ├── cainjection_in_grafanafolders.yaml
│ │ ├── cainjection_in_grafananotificationpolicyroutes.yaml
│ │ ├── cainjection_in_grafanas.yaml
│ │ ├── webhook_in_grafanaalertrulegroups.yaml
│ │ ├── webhook_in_grafanacontactpoints.yaml
│ │ ├── webhook_in_grafanadashboards.yaml
│ │ ├── webhook_in_grafanadatasources.yaml
│ │ ├── webhook_in_grafanafolders.yaml
│ │ ├── webhook_in_grafananotificationpolicyroutes.yaml
│ │ └── webhook_in_grafanas.yaml
├── default
│ ├── kustomization.yaml
│ ├── manager_config_patch.yaml
│ └── metrics_service.yaml
├── manager
│ ├── controller_manager_config.yaml
│ ├── kustomization.yaml
│ └── manager.yaml
├── manifests
│ ├── bases
│ │ └── grafana-operator.clusterserviceversion.yaml
│ └── kustomization.yaml
├── prometheus
│ ├── kustomization.yaml
│ └── monitor.yaml
├── rbac
│ ├── grafana_editor_role.yaml
│ ├── grafana_viewer_role.yaml
│ ├── grafanaalertrulegroup_editor_role.yaml
│ ├── grafanaalertrulegroup_viewer_role.yaml
│ ├── grafanacontactpoint_editor_role.yaml
│ ├── grafanacontactpoint_viewer_role.yaml
│ ├── grafanadashboard_editor_role.yaml
│ ├── grafanadashboard_viewer_role.yaml
│ ├── grafanadatasource_editor_role.yaml
│ ├── grafanadatasource_viewer_role.yaml
│ ├── grafanafolder_editor_role.yaml
│ ├── grafanafolder_viewer_role.yaml
│ ├── grafanalibrarypanel_editor_role.yaml
│ ├── grafanalibrarypanel_viewer_role.yaml
│ ├── grafananotificationpolicyroute_editor_role.yaml
│ ├── grafananotificationpolicyroute_viewer_role.yaml
│ ├── kustomization.yaml
│ ├── leader_election_role.yaml
│ ├── leader_election_role_binding.yaml
│ ├── role.yaml
│ ├── role_binding.yaml
│ └── service_account.yaml
├── samples
│ ├── grafana_v1beta1_grafana.yaml
│ ├── grafana_v1beta1_grafanaalertrulegroup.yaml
│ ├── grafana_v1beta1_grafanacontactpoint.yaml
│ ├── grafana_v1beta1_grafanadashboard.yaml
│ ├── grafana_v1beta1_grafanadatasource.yaml
│ ├── grafana_v1beta1_grafanafolder.yaml
│ ├── grafana_v1beta1_grafanalibrarypanel.yaml
│ ├── grafana_v1beta1_grafanamutetiming.yaml
│ ├── grafana_v1beta1_grafananotificationpolicy.yaml
│ ├── grafana_v1beta1_grafananotificationpolicyroute.yaml
│ ├── grafana_v1beta1_grafananotificationtemplate.yaml
│ └── kustomization.yaml
└── scorecard
│ ├── bases
│ └── config.yaml
│ ├── kustomization.yaml
│ └── patches
│ ├── basic.config.yaml
│ └── olm.config.yaml
├── controllers
├── alertrulegroup_controller.go
├── alertrulegroup_controller_test.go
├── autodetect
│ ├── main.go
│ └── main_test.go
├── client
│ ├── common.go
│ ├── grafana_client.go
│ ├── grafana_client_test.go
│ ├── http_client.go
│ ├── round_tripper.go
│ └── tls.go
├── config
│ ├── grafana_ini.go
│ ├── grafana_ini_test.go
│ └── operator_constants.go
├── contactpoint_controller.go
├── contactpoint_controller_test.go
├── content
│ ├── cache
│ │ ├── cache.go
│ │ └── cache_test.go
│ ├── fetchers
│ │ ├── configmap_fetcher.go
│ │ ├── grafana_com_fetcher.go
│ │ ├── grafana_com_fetcher_test.go
│ │ ├── jsonnet_fetcher.go
│ │ ├── jsonnet_fetcher_test.go
│ │ ├── suite_test.go
│ │ ├── url_fetcher.go
│ │ └── url_fetcher_test.go
│ ├── resolver.go
│ ├── resolver_test.go
│ ├── source.go
│ └── suite_test.go
├── controller_shared.go
├── controller_shared_test.go
├── dashboard_controller.go
├── dashboard_controller_test.go
├── datasource_controller.go
├── datasource_controller_test.go
├── folder_controller.go
├── folder_controller_test.go
├── grafana_controller.go
├── librarypanel_controller.go
├── librarypanel_controller_test.go
├── metrics
│ └── metrics.go
├── model
│ ├── dashboard_resources.go
│ ├── grafana_resources.go
│ └── utils.go
├── mutetiming_controller.go
├── mutetiming_controller_test.go
├── notificationpolicy_controller.go
├── notificationpolicy_controller_test.go
├── notificationtemplate_controller.go
├── notificationtemplate_controller_test.go
├── reconcilers
│ ├── grafana
│ │ ├── admin_secret_reconciler.go
│ │ ├── admin_secret_reconciler_test.go
│ │ ├── complete_reconciler.go
│ │ ├── config_reconciler.go
│ │ ├── deployment_reconciler.go
│ │ ├── deployment_reconciler_test.go
│ │ ├── ingress_reconciler.go
│ │ ├── plugins_reconciler.go
│ │ ├── pvc_reconciler.go
│ │ ├── service_account_reconciler.go
│ │ ├── service_reconciler.go
│ │ ├── service_reconciler_test.go
│ │ └── suite_test.go
│ └── reconciler.go
└── suite_test.go
├── deploy
├── helm
│ ├── cr.yaml
│ └── grafana-operator
│ │ ├── .helmignore
│ │ ├── Chart.yaml
│ │ ├── README.md
│ │ ├── README.md.gotmpl
│ │ ├── crds
│ │ ├── grafana.integreatly.org_grafanaalertrulegroups.yaml
│ │ ├── grafana.integreatly.org_grafanacontactpoints.yaml
│ │ ├── grafana.integreatly.org_grafanadashboards.yaml
│ │ ├── grafana.integreatly.org_grafanadatasources.yaml
│ │ ├── grafana.integreatly.org_grafanafolders.yaml
│ │ ├── grafana.integreatly.org_grafanalibrarypanels.yaml
│ │ ├── grafana.integreatly.org_grafanamutetimings.yaml
│ │ ├── grafana.integreatly.org_grafananotificationpolicies.yaml
│ │ ├── grafana.integreatly.org_grafananotificationpolicyroutes.yaml
│ │ ├── grafana.integreatly.org_grafananotificationtemplates.yaml
│ │ └── grafana.integreatly.org_grafanas.yaml
│ │ ├── files
│ │ ├── dashboard.json
│ │ ├── rbac-openshift.yaml
│ │ └── rbac.yaml
│ │ ├── templates
│ │ ├── _helpers.tpl
│ │ ├── dashboard.yaml
│ │ ├── deployment.yaml
│ │ ├── extraobjects.yaml
│ │ ├── rbac.yaml
│ │ ├── service.yaml
│ │ ├── serviceaccount.yaml
│ │ └── servicemonitor.yaml
│ │ └── values.yaml
└── kustomize
│ ├── README.md
│ ├── base
│ ├── crds.yaml
│ ├── deployment.yaml
│ ├── kustomization.yaml
│ ├── namespace.yaml
│ ├── role.yaml
│ ├── rolebinding.yaml
│ ├── service.yaml
│ └── serviceaccount.yaml
│ └── overlays
│ ├── chainsaw
│ ├── deployment-patch.yaml
│ └── kustomization.yaml
│ ├── cluster_scoped
│ └── kustomization.yaml
│ └── namespace_scoped
│ ├── deployment.yaml
│ └── kustomization.yaml
├── docs
├── README.md
├── _index.html
├── about
│ └── _index.html
├── blog
│ ├── _index.md
│ ├── flux-gitops
│ │ ├── grafana-operator.yaml
│ │ ├── grafana.yaml
│ │ └── grafana
│ │ │ ├── dashboard.yaml
│ │ │ ├── grafana.yaml
│ │ │ └── kustomization.yaml
│ ├── history-and-operator-reach.md
│ ├── kustomize-installation.md
│ ├── operator-migration-to-upstream-grafana.md
│ ├── v4-v5-migration.md
│ ├── v5-getting-started.md
│ └── v5-intro.md
└── docs
│ ├── _index.md
│ ├── alerting
│ ├── _index.md
│ ├── alert-rule-groups.md
│ ├── contact-points.md
│ ├── dynamic-notification-policy.png
│ ├── notification-policies.md
│ ├── notification-policy-tree.png
│ ├── notification-routing.png
│ └── overview-page.png
│ ├── api.md
│ ├── dashboards.md
│ ├── datasources.md
│ ├── folder.md
│ ├── grafana.md
│ ├── installation
│ ├── _index.md
│ ├── kustomize.md
│ └── ops-and-monitoring.md
│ ├── library-panels.md
│ ├── overview.md
│ ├── proposals
│ ├── 001 external grafana instance.md
│ ├── 002-alerting-support.md
│ ├── 003-grafanaserviceaccount-crd.md
│ ├── 004-grafanafolder-parent-folder-management.md
│ ├── 005-grafanadashboard-folder-management-strategy-update.md
│ ├── 006-ssl-specification-in-grafanaexternal.md
│ ├── 007-grafanalibrarypanel-crd.md
│ └── _index.md
│ ├── quick-start.md
│ └── security.md
├── embeds
├── README.md
├── grafana_embeds.go
├── grafonnet-lib
│ ├── grafonnet-7.0
│ │ ├── DOCS.md
│ │ ├── dashboard.libsonnet
│ │ ├── grafana.libsonnet
│ │ ├── panel
│ │ │ ├── gauge.libsonnet
│ │ │ ├── graph.libsonnet
│ │ │ ├── row.libsonnet
│ │ │ ├── stat.libsonnet
│ │ │ ├── table.libsonnet
│ │ │ └── text.libsonnet
│ │ ├── target
│ │ │ └── prometheus.libsonnet
│ │ └── template
│ │ │ ├── custom.libsonnet
│ │ │ ├── datasource.libsonnet
│ │ │ └── query.libsonnet
│ └── grafonnet
│ │ ├── alert_condition.libsonnet
│ │ ├── alertlist.libsonnet
│ │ ├── annotation.libsonnet
│ │ ├── bar_gauge_panel.libsonnet
│ │ ├── cloudmonitoring.libsonnet
│ │ ├── cloudwatch.libsonnet
│ │ ├── dashboard.libsonnet
│ │ ├── dashlist.libsonnet
│ │ ├── elasticsearch.libsonnet
│ │ ├── gauge_panel.libsonnet
│ │ ├── grafana.libsonnet
│ │ ├── graph_panel.libsonnet
│ │ ├── graphite.libsonnet
│ │ ├── heatmap_panel.libsonnet
│ │ ├── influxdb.libsonnet
│ │ ├── link.libsonnet
│ │ ├── log_panel.libsonnet
│ │ ├── loki.libsonnet
│ │ ├── pie_chart_panel.libsonnet
│ │ ├── pluginlist.libsonnet
│ │ ├── prometheus.libsonnet
│ │ ├── row.libsonnet
│ │ ├── singlestat.libsonnet
│ │ ├── sql.libsonnet
│ │ ├── stat_panel.libsonnet
│ │ ├── table_panel.libsonnet
│ │ ├── template.libsonnet
│ │ ├── text.libsonnet
│ │ ├── timepicker.libsonnet
│ │ └── transformation.libsonnet
└── testing
│ ├── dashboard.json
│ ├── dashboard.jsonnet
│ ├── dashboard_with_envs.jsonnet
│ ├── dashboard_with_provided_envs.json
│ ├── jsonnetProjectWithRuntimeRaw.tar.gz
│ └── jsonnetProjectWithRuntimeRaw
│ └── dashboard_with_envs.jsonnet
├── examples
├── _index.md
├── alertrulegroups
│ ├── README.md
│ └── resources.yaml
├── basic
│ ├── README.md
│ └── resources.yaml
├── configmaps_sidecar
│ ├── README.md
│ └── resources.yaml
├── contactpoint_override
│ ├── README.md
│ └── resources.yaml
├── credential_config
│ ├── README.md
│ └── resources.yaml
├── credential_secret
│ ├── README.md
│ └── resources.yaml
├── crossnamespace
│ ├── README.md
│ └── resources.yaml
├── dashboard_from_configmap
│ ├── README.md
│ └── resources.yaml
├── dashboard_from_grafana_com
│ ├── README.md
│ └── resources.yaml
├── dashboard_from_url
│ ├── README.md
│ ├── dashboard.json
│ └── resources.yaml
├── dashboard_gzipped
│ ├── README.md
│ ├── dashboard.json
│ └── resources.yaml
├── dashboard_with_custom_folder
│ ├── README.md
│ └── resources.yaml
├── dashboard_with_external_dependencies
│ ├── README.md
│ ├── dashboard_project
│ │ ├── dashboard.jsonnet
│ │ ├── jsonnetfile.json
│ │ ├── jsonnetfile.lock.json
│ │ └── util
│ │ │ ├── datasources.libsonnet
│ │ │ ├── envs.libsonnet
│ │ │ ├── g.libsonnet
│ │ │ ├── panels.libsonnet
│ │ │ ├── queries.libsonnet
│ │ │ ├── styles.libsonnet
│ │ │ └── variables.libsonnet
│ └── resources.yaml
├── datasource_mapping
│ ├── README.md
│ └── resources.yaml
├── datasource_types
│ ├── README.md
│ ├── postgresql.yaml
│ └── prometheus.yaml
├── datasource_variables
│ ├── README.md
│ └── resources.yaml
├── external_grafana
│ ├── README.md
│ └── resources.yaml
├── folder
│ ├── README.md
│ └── resources.yaml
├── grafana_deployment
│ ├── README.md
│ └── resources.yaml
├── grafana_google_sso
│ ├── README.md
│ └── resources.yaml
├── grafana_keycloak_sso
│ ├── README.md
│ └── resources.yaml
├── ingress_http
│ ├── README.md
│ └── resources.yaml
├── ingress_https
│ ├── README.md
│ ├── cert.yaml
│ ├── dashboard.yaml
│ └── resources.yaml
├── jsonnet
│ ├── README.md
│ └── resources.yaml
├── k3d
│ ├── README.md
│ └── resources.yaml
├── ldap
│ ├── README.md
│ └── resources.yaml
├── multiple_replicas
│ ├── README.md
│ └── resources.yaml
├── mute_timing
│ ├── README.md
│ └── resources.yaml
├── notification-policy
│ ├── resources.yaml
│ └── routes.yaml
├── notification_template
│ ├── README.md
│ └── resources.yaml
├── notifications-full
│ ├── contact-points.yaml
│ ├── folder.yaml
│ ├── kubernetes-alert-rules.yaml
│ ├── notification-policy.yaml
│ └── security-alert-rules.yaml
├── oauth_proxy
│ ├── README.md
│ └── resources.yaml
├── openshift
│ ├── README.md
│ └── resources.yaml
├── persistent_volume
│ ├── README.md
│ └── resources.yaml
└── plugins
│ ├── README.md
│ ├── dashboard.yaml
│ └── datasource.yaml
├── go.mod
├── go.sum
├── hack
├── add-openshift-annotations.sh
├── boilerplate.go.txt
└── kind
│ ├── populate-kind-cluster.sh
│ ├── resources
│ ├── cluster.yaml
│ ├── crd-ns
│ │ ├── grafana-dashboard.yaml
│ │ ├── grafana-datasource.yaml
│ │ └── kustomization.yaml
│ └── default
│ │ ├── deployment-thanos.yaml
│ │ ├── grafana-contactpoint.yaml
│ │ ├── grafana-dashboard.yaml
│ │ ├── grafana-datasource-thanos.yaml
│ │ ├── grafana-datasource.yaml
│ │ ├── grafana-notification-policy.yaml
│ │ ├── grafana.yaml
│ │ ├── kustomization.yaml
│ │ ├── secret.yaml
│ │ └── serviceaccount.yaml
│ └── start-kind.sh
├── hugo
├── .gitignore
├── Makefile
├── README.md
├── archetypes
│ └── default.md
├── assets
│ └── scss
│ │ ├── _styles_project.scss
│ │ └── _variables_project.scss
├── config.yaml
├── go.mod
├── go.sum
├── package-lock.json
├── package.json
└── templates
│ └── frontmatter-grafana-operator.tmpl
├── labs
├── audit
│ ├── README.md
│ ├── audit-policy.yaml
│ ├── collect-audit-stats.sh
│ └── kind-config.yaml
└── benchmark
│ ├── Makefile
│ ├── create_resources.lua
│ ├── dashboards
│ └── .gitkeep
│ ├── datasources
│ └── .gitkeep
│ └── prometheus.yml
├── main.go
├── media
├── logo.svg
├── logo_small.svg
└── slack.png
└── tests
├── e2e
├── conditions
│ ├── 02-apply-failed-assertions.yaml
│ ├── 03-additional-invalid-spec.yaml
│ ├── 03-invalid-spec-assertions.yaml
│ ├── 03-testdata-invalid-specs.yaml
│ └── chainsaw-test.yaml
├── example-test
│ ├── 00-assert.yaml
│ ├── 00-create-external-grafana.yaml
│ ├── 00-create-grafana.yaml
│ ├── 00-create-versioned-grafana.yaml
│ ├── 01-assert.yaml
│ ├── 01-datasource.yaml
│ ├── 02-assert.yaml
│ ├── 03-assert.yaml
│ ├── 03-dashboard.yaml
│ ├── 04-assert.yaml
│ ├── 04-dashboard.yaml
│ ├── 05-assert.yaml
│ ├── 05-dashboard.yaml
│ ├── 06-assert.yaml
│ ├── 06-dashboard.yaml
│ ├── 07-assert.yaml
│ ├── 07-jsonnet.yaml
│ ├── 08-alert-folder.yaml
│ ├── 08-alert-rule-group.yaml
│ ├── 08-assert.yaml
│ ├── 08-folder-assert.yaml
│ ├── 09-assert.yaml
│ ├── 09-contactpoint.yaml
│ ├── 10-assert-contact-points.yaml
│ ├── 10-assert.yaml
│ ├── 10-contact-points.yaml
│ ├── 10-notification-policy.yaml
│ ├── 11-assert.yaml
│ ├── 11-tls.yaml
│ ├── 12-assert.yaml
│ ├── 12-notification-template.yaml
│ ├── 13-assert.yaml
│ ├── 13-mute-timing.yaml
│ ├── 14-assert.yaml
│ ├── 14-library-panel.yaml
│ ├── 15-assert.yaml
│ ├── 15-library-panel.yaml
│ └── chainsaw-test.yaml
├── examples
│ ├── basic
│ │ ├── assertions.yaml
│ │ ├── chainsaw-test.yaml
│ │ └── resources.yaml
│ ├── crossnamespace
│ │ ├── assertions.yaml
│ │ ├── chainsaw-test.yaml
│ │ └── resources.yaml
│ └── dashboard_immutable_uid
│ │ ├── base-resources.yaml
│ │ └── chainsaw-test.yaml.disabled
├── force_delete_folder
│ └── chainsaw-test.yaml
├── testdata-assertions.yaml
└── testdata-resources.yaml
└── example-resources.yaml
/.chainsaw.yaml:
--------------------------------------------------------------------------------
1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/configuration-chainsaw-v1alpha1.json
2 | apiVersion: chainsaw.kyverno.io/v1alpha2
3 | kind: Configuration
4 | metadata:
5 | name: configuration
6 | spec:
7 | error:
8 | catch:
9 | - describe:
10 | apiVersion: grafana.integreatly.org/v1beta1
11 | kind: grafana-operator
12 | - podLogs:
13 | namespace: grafana-operator-system
14 | tail: 100
15 | timeouts:
16 | assert: 2m0s
17 | cleanup: 3m0s
18 | delete: 2m0s
19 | error: 2m0s
20 | exec: 2m0s
21 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
2 | # Ignore build and test binaries.
3 | bin/
4 | testbin/
5 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us triage the problem and find a fix.
4 | labels: needs triage
5 | type: 'bug'
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **Version**
14 | Full semver version of the operator being used e.g. v4.10.0, v5.0.0-rc0
15 |
16 | **To Reproduce**
17 | Steps to reproduce the behavior:
18 |
19 | 1. Go to '...'
20 | 2. Click on '....'
21 | 3. Scroll down to '....'
22 | 4. See error
23 | etc.
24 |
25 | **Expected behavior**
26 | A clear and concise description of what you expected to happen.
27 |
28 | **Suspect component/Location where the bug might be occurring**
29 | Please provide this if you know where this bug might occur otherwise leave as `unknown`
30 |
31 | **Screenshots**
32 | If applicable, add screenshots to help explain your problem.
33 |
34 | **Runtime (please complete the following information):**
35 |
36 |
37 |
38 | - OS: [e.g. Linux,Fedora,Mac]
39 | - Grafana Operator Version [e.g. v5.0.0]
40 | - Environment: [e.g Openshift,Kubernetes,minikube etc. please specify versions]
41 | - Deployment type: [e.g Openshift OLM/Helm/kustomize]
42 | - Other: [Other variables/things that might be relevant to this bug, versions of other services e.g. operator-sdk]
43 |
44 | **Additional context**
45 | Add any other context about the problem here.
46 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | labels: needs triage
5 | type: 'enhancement'
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **(If applicable)If your feature request solves a bug please provide a link to the community issue**
14 |
15 | **Describe the solution you'd like**
16 | A clear and concise description of what you want to happen.
17 |
18 | **Describe alternatives you've considered**
19 | A clear and concise description of any alternative solutions or features you've considered.
20 |
21 | **Additional context**
22 | Add any other context or screenshots about the feature request here.
23 |
24 | **Existing solutions**
25 | If applicable please provide a link to an existing solution from a different project
26 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | updates:
4 | - package-ecosystem: "github-actions"
5 | directory: "/"
6 | schedule:
7 | interval: "weekly"
8 | groups:
9 | gha:
10 | update-types:
11 | - minor
12 | - patch
13 |
14 | - package-ecosystem: "gomod"
15 | directory: "/"
16 | schedule:
17 | interval: "weekly"
18 | groups:
19 | gomod:
20 | update-types:
21 | - minor
22 | - patch
23 |
--------------------------------------------------------------------------------
/.github/labeler.yml:
--------------------------------------------------------------------------------
1 | documentation:
2 | - changed-files:
3 | - any-glob-to-any-file: docs/**
4 | - any-glob-to-any-file: examples/**
5 |
6 | feature:
7 | - head-branch: ['^feature', 'feature', '^feat']
8 |
9 | bugfix:
10 | - head-branch: ['^fix', 'fix', '^bugfix']
11 |
12 | chore:
13 | - head-branch: ['^chore', '^ci', ^refactor]
14 |
15 | test:
16 | - head-branch: ['^test']
17 |
--------------------------------------------------------------------------------
/.github/release.yml:
--------------------------------------------------------------------------------
1 | changelog:
2 | exclude:
3 | labels:
4 | - documentation
5 | - chore
6 | - refactor
7 | - release-ignore
8 | categories:
9 | - title: Features
10 | labels:
11 | - 'feature'
12 | - title: Fixes
13 | labels:
14 | - 'bugfix'
15 | - title: Dependencies
16 | labels:
17 | - dependencies
18 |
--------------------------------------------------------------------------------
/.github/workflows/labeler.yaml:
--------------------------------------------------------------------------------
1 | name: "Pull Request Labeler"
2 | on: # zizmor: ignore[dangerous-triggers] pull_request target used here as per recommendation from GitHub in actions/labeler
3 | - pull_request_target
4 |
5 | jobs:
6 | labeler:
7 | permissions:
8 | contents: read
9 | pull-requests: write
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5
13 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | # This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
2 | #
3 | # You can adjust the behavior by modifying this file.
4 | # For more information, see:
5 | # https://github.com/actions/stale
6 | name: Mark stale issues and pull requests
7 |
8 | on:
9 | schedule:
10 | - cron: "0 1 * * *"
11 |
12 | jobs:
13 | stale:
14 | runs-on: ubuntu-latest
15 | permissions:
16 | issues: write
17 | pull-requests: write
18 |
19 | steps:
20 | - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9
21 | with:
22 | repo-token: ${{ secrets.GITHUB_TOKEN }}
23 | stale-issue-message: "This issue hasn't been updated for a while, marking as stale, please respond within the next 7 days to remove this label"
24 | stale-pr-message: "This PR hasn't been updated for a while, marking as stale"
25 | stale-issue-label: "stale"
26 | stale-pr-label: "stale"
27 | # mark issues and PR's as stale after this many days
28 | days-before-stale: 30
29 | # close issues and PR's that are marked as stale after this many days
30 | days-before-close: 7
31 | # don't mark triaged issues as stale
32 | exempt-issue-labels: "triage/accepted"
33 | # unmark as stale if someone responds
34 | remove-issue-stale-when-updated: true
35 | remove-pr-stale-when-updated: true
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Binaries for programs and plugins
3 | *.exe
4 | *.exe~
5 | *.dll
6 | *.so
7 | *.dylib
8 | bin
9 | testbin/*
10 |
11 | # Test binary, build with `go test -c`
12 | *.test
13 |
14 | # Output of the go coverage tool, specifically when used with LiteIDE
15 | *.out
16 |
17 | # Kubernetes Generated files - skip generated files, except for vendored files
18 |
19 | !vendor/**/zz_generated.*
20 | vendor
21 |
22 | # editor and IDE paraphernalia
23 | .idea
24 | *.swp
25 | *.swo
26 | *~
27 |
28 | .vscode/
29 | .DS_Store
30 |
31 | # Audit lab
32 | kube-apiserver-audit.log
33 | labs/benchmark/dashboards
34 | labs/benchmark/datasources
35 |
36 | # kind artifacts
37 | kubeconfig
38 |
--------------------------------------------------------------------------------
/.ko.yaml:
--------------------------------------------------------------------------------
1 | builds:
2 | - id: grafana-operator
3 | dir: .
4 | main: .
5 | flags:
6 | - '-trimpath'
7 | ldflags:
8 | - -X github.com/grafana/grafana-operator/v5/embeds.Version={{.Git.Tag}}
9 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/pre-commit/pre-commit-hooks
3 | rev: v5.0.0
4 | hooks:
5 | - id: check-shebang-scripts-are-executable
6 |
7 | - id: end-of-file-fixer
8 |
9 | - id: mixed-line-ending
10 | args: [--fix=lf]
11 |
12 | - id: trailing-whitespace
13 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @NissesSenap @weisdd @ishanjainn @theSuess @hubeadmin @pb82 @Baarsgaard
2 |
--------------------------------------------------------------------------------
/api/folderreferencer.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
4 |
5 | type FolderReferencer interface {
6 | FolderRef() string
7 | FolderUID() string
8 | FolderNamespace() string
9 | ConditionsResource
10 | }
11 |
12 | type ConditionsResource interface {
13 | Conditions() *[]metav1.Condition
14 | CurrentGeneration() int64
15 | }
16 |
--------------------------------------------------------------------------------
/api/v1beta1/groupversion_info.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package v1beta1 contains API Schema definitions for the grafana v1beta1 API group
18 | // +kubebuilder:object:generate=true
19 | // +groupName=grafana.integreatly.org
20 | package v1beta1
21 |
22 | import (
23 | "k8s.io/apimachinery/pkg/runtime/schema"
24 | "sigs.k8s.io/controller-runtime/pkg/scheme"
25 | )
26 |
27 | var (
28 | // GroupVersion is group version used to register these objects
29 | GroupVersion = schema.GroupVersion{Group: "grafana.integreatly.org", Version: "v1beta1"}
30 |
31 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme
32 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
33 |
34 | // AddToScheme adds the types in this group-version to the given scheme.
35 | AddToScheme = SchemeBuilder.AddToScheme
36 | )
37 |
--------------------------------------------------------------------------------
/api/v1beta1/plugin_list_test.go:
--------------------------------------------------------------------------------
1 | package v1beta1
2 |
3 | import (
4 | "strings"
5 | "testing"
6 | "testing/quick"
7 |
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | func TestPluginString(t *testing.T) {
12 | err := quick.Check(func(a string, b string, c string) bool {
13 | if strings.Contains(a, ",") || strings.Contains(b, ",") || strings.Contains(c, ",") {
14 | return true // skip plugins with ,
15 | }
16 | pl := PluginList{
17 | {
18 | Name: a,
19 | Version: "7.2",
20 | },
21 | {
22 | Name: b,
23 | Version: "2.2",
24 | },
25 | {
26 | Name: c,
27 | Version: "6.7",
28 | },
29 | }
30 | out := pl.String()
31 | split := strings.Split(out, ",")
32 | if len(split) != 3 {
33 | return false
34 | }
35 | if split[0] > split[1] {
36 | return false
37 | }
38 | if split[1] > split[2] {
39 | return false
40 | }
41 | return true
42 | }, nil)
43 | if err != nil {
44 | t.Errorf("plugin list was not sorted: %s", err.Error())
45 | }
46 | }
47 |
48 | func TestPluginSanitize(t *testing.T) {
49 | pl := PluginList{
50 | {
51 | Name: "plugin-a",
52 | Version: "1.0.0",
53 | },
54 | {
55 | Name: "plugin-b",
56 | Version: "2.0.0",
57 | },
58 | {
59 | Name: "plugin-a",
60 | Version: "3.0.0",
61 | },
62 | }
63 | sanitized := pl.Sanitize()
64 | assert.Len(t, sanitized, 2)
65 | }
66 |
--------------------------------------------------------------------------------
/bundle.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM scratch
2 |
3 | # Core bundle labels.
4 | LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1
5 | LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/
6 | LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/
7 | LABEL operators.operatorframework.io.bundle.package.v1=grafana-operator
8 | LABEL operators.operatorframework.io.bundle.channels.v1=v5
9 | LABEL operators.operatorframework.io.bundle.channel.default.v1=v5
10 | LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.32.0
11 | LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1
12 | LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v3
13 |
14 | # Copy files to locations specified by labels.
15 | COPY bundle/manifests /manifests/
16 | COPY bundle/metadata /metadata/
17 | LABEL com.redhat.openshift.versions="v4.11-v4.15"
18 |
--------------------------------------------------------------------------------
/bundle/manifests/grafana-operator-manager-config_v1_configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | data:
3 | controller_manager_config.yaml: |
4 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1
5 | kind: ControllerManagerConfig
6 | health:
7 | healthProbeBindAddress: :8081
8 | metrics:
9 | bindAddress: 127.0.0.1:8080
10 | webhook:
11 | port: 9443
12 | leaderElection:
13 | leaderElect: true
14 | resourceName: f75f3bba.integreatly.org
15 | kind: ConfigMap
16 | metadata:
17 | name: grafana-operator-manager-config
18 |
--------------------------------------------------------------------------------
/bundle/manifests/grafana-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | creationTimestamp: null
5 | name: grafana-operator-metrics-reader
6 | rules:
7 | - nonResourceURLs:
8 | - /metrics
9 | verbs:
10 | - get
11 |
--------------------------------------------------------------------------------
/bundle/manifests/grafana-operator-operator-metrics-service_v1_service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | creationTimestamp: null
5 | labels:
6 | app.kubernetes.io/managed-by: olm
7 | app.kubernetes.io/name: grafana-operator
8 | name: grafana-operator-operator-metrics-service
9 | spec:
10 | ports:
11 | - name: metrics
12 | port: 8443
13 | protocol: TCP
14 | targetPort: metrics
15 | - port: 8888
16 | targetPort: pprof
17 | protocol: TCP
18 | name: pprof
19 | selector:
20 | app.kubernetes.io/managed-by: olm
21 | app.kubernetes.io/name: grafana-operator
22 | status:
23 | loadBalancer: {}
24 |
--------------------------------------------------------------------------------
/bundle/metadata/annotations.yaml:
--------------------------------------------------------------------------------
1 | annotations:
2 | # Core bundle annotations.
3 | operators.operatorframework.io.bundle.mediatype.v1: registry+v1
4 | operators.operatorframework.io.bundle.manifests.v1: manifests/
5 | operators.operatorframework.io.bundle.metadata.v1: metadata/
6 | operators.operatorframework.io.bundle.package.v1: grafana-operator
7 | operators.operatorframework.io.bundle.channels.v1: v5
8 | operators.operatorframework.io.bundle.channel.default.v1: v5
9 | operators.operatorframework.io.metrics.builder: operator-sdk-v1.32.0
10 | operators.operatorframework.io.metrics.mediatype.v1: metrics+v1
11 | operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v3
12 |
13 | # OpenShift specific annotations
14 | com.redhat.openshift.versions: "v4.11-v4.15"
15 |
--------------------------------------------------------------------------------
/catalog-info.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: backstage.io/v1alpha1
2 | kind: Component
3 | metadata:
4 | name: grafana-operator
5 | title: grafana-operator
6 | description: |
7 | An operator for Grafana that installs and manages Grafana instances, Dashboards and Datasources through Kubernetes/OpenShift CRs
8 | links:
9 | - title: Internal Slack Channel
10 | url: https://raintank-corp.slack.com/archives/C06K5TJ469W
11 | - title: Community Slack Channel
12 | url: https://grafana.slack.com/archives/C0692KN29K3
13 | - title: Kubernetes Slack Channel
14 | url: https://kubernetes.slack.com/archives/C019A1KTYKC
15 | annotations:
16 | backstage.io/techdocs-ref: dir:.
17 | github.com/project-slug: grafana/grafana-operator
18 | spec:
19 | type: tool
20 | owner: group:default/devex
21 | lifecycle: production
22 |
--------------------------------------------------------------------------------
/config/crd/kustomizeconfig.yaml:
--------------------------------------------------------------------------------
1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD
2 | nameReference:
3 | - kind: Service
4 | version: v1
5 | fieldSpecs:
6 | - kind: CustomResourceDefinition
7 | version: v1
8 | group: apiextensions.k8s.io
9 | path: spec/conversion/webhook/clientConfig/service/name
10 |
11 | namespace:
12 | - kind: CustomResourceDefinition
13 | version: v1
14 | group: apiextensions.k8s.io
15 | path: spec/conversion/webhook/clientConfig/service/namespace
16 | create: false
17 |
18 | varReference:
19 | - path: metadata/annotations
20 |
--------------------------------------------------------------------------------
/config/crd/patches/cainjection_in_grafanaalertrulegroups.yaml:
--------------------------------------------------------------------------------
1 | # The following patch adds a directive for certmanager to inject CA into the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
7 | name: grafanaalertrulegroups.grafana.integreatly.org
8 |
--------------------------------------------------------------------------------
/config/crd/patches/cainjection_in_grafanacontactpoints.yaml:
--------------------------------------------------------------------------------
1 | # The following patch adds a directive for certmanager to inject CA into the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
7 | name: grafanacontactpoints.grafana.integreatly.org
8 |
--------------------------------------------------------------------------------
/config/crd/patches/cainjection_in_grafanadashboards.yaml:
--------------------------------------------------------------------------------
1 | # The following patch adds a directive for certmanager to inject CA into the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
7 | name: grafanadashboards.grafana.integreatly.org
8 |
--------------------------------------------------------------------------------
/config/crd/patches/cainjection_in_grafanadatasources.yaml:
--------------------------------------------------------------------------------
1 | # The following patch adds a directive for certmanager to inject CA into the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
7 | name: grafanadatasources.grafana.integreatly.org
8 |
--------------------------------------------------------------------------------
/config/crd/patches/cainjection_in_grafanafolders.yaml:
--------------------------------------------------------------------------------
1 | # The following patch adds a directive for certmanager to inject CA into the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
7 | name: grafanafolders.grafana.integreatly.org
8 |
--------------------------------------------------------------------------------
/config/crd/patches/cainjection_in_grafananotificationpolicyroutes.yaml:
--------------------------------------------------------------------------------
1 | # The following patch adds a directive for certmanager to inject CA into the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
7 | name: grafananotificationpolicyroutes.grafana.integreatly.org
8 |
--------------------------------------------------------------------------------
/config/crd/patches/cainjection_in_grafanas.yaml:
--------------------------------------------------------------------------------
1 | # The following patch adds a directive for certmanager to inject CA into the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
7 | name: grafanas.grafana.integreatly.org
8 |
--------------------------------------------------------------------------------
/config/crd/patches/webhook_in_grafanaalertrulegroups.yaml:
--------------------------------------------------------------------------------
1 | # The following patch enables a conversion webhook for the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | name: grafanaalertrulegroups.grafana.integreatly.org
6 | spec:
7 | conversion:
8 | strategy: Webhook
9 | webhook:
10 | clientConfig:
11 | service:
12 | namespace: system
13 | name: webhook-service
14 | path: /convert
15 | conversionReviewVersions:
16 | - v1
17 |
--------------------------------------------------------------------------------
/config/crd/patches/webhook_in_grafanacontactpoints.yaml:
--------------------------------------------------------------------------------
1 | # The following patch enables a conversion webhook for the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | name: grafanacontactpoints.grafana.integreatly.org
6 | spec:
7 | conversion:
8 | strategy: Webhook
9 | webhook:
10 | clientConfig:
11 | service:
12 | namespace: system
13 | name: webhook-service
14 | path: /convert
15 | conversionReviewVersions:
16 | - v1
17 |
--------------------------------------------------------------------------------
/config/crd/patches/webhook_in_grafanadashboards.yaml:
--------------------------------------------------------------------------------
1 | # The following patch enables a conversion webhook for the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | name: grafanadashboards.grafana.integreatly.org
6 | spec:
7 | conversion:
8 | strategy: Webhook
9 | webhook:
10 | clientConfig:
11 | service:
12 | namespace: system
13 | name: webhook-service
14 | path: /convert
15 | conversionReviewVersions:
16 | - v1
17 |
--------------------------------------------------------------------------------
/config/crd/patches/webhook_in_grafanadatasources.yaml:
--------------------------------------------------------------------------------
1 | # The following patch enables a conversion webhook for the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | name: grafanadatasources.grafana.integreatly.org
6 | spec:
7 | conversion:
8 | strategy: Webhook
9 | webhook:
10 | clientConfig:
11 | service:
12 | namespace: system
13 | name: webhook-service
14 | path: /convert
15 | conversionReviewVersions:
16 | - v1
17 |
--------------------------------------------------------------------------------
/config/crd/patches/webhook_in_grafanafolders.yaml:
--------------------------------------------------------------------------------
1 | # The following patch enables a conversion webhook for the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | name: grafanafolders.grafana.integreatly.org
6 | spec:
7 | conversion:
8 | strategy: Webhook
9 | webhook:
10 | clientConfig:
11 | service:
12 | namespace: system
13 | name: webhook-service
14 | path: /convert
15 | conversionReviewVersions:
16 | - v1
17 |
--------------------------------------------------------------------------------
/config/crd/patches/webhook_in_grafananotificationpolicyroutes.yaml:
--------------------------------------------------------------------------------
1 | # The following patch enables a conversion webhook for the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | name: grafananotificationpolicyroutes.grafana.integreatly.org
6 | spec:
7 | conversion:
8 | strategy: Webhook
9 | webhook:
10 | clientConfig:
11 | service:
12 | namespace: system
13 | name: webhook-service
14 | path: /convert
15 | conversionReviewVersions:
16 | - v1
17 |
--------------------------------------------------------------------------------
/config/crd/patches/webhook_in_grafanas.yaml:
--------------------------------------------------------------------------------
1 | # The following patch enables a conversion webhook for the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | name: grafanas.grafana.integreatly.org
6 | spec:
7 | conversion:
8 | strategy: Webhook
9 | webhook:
10 | clientConfig:
11 | service:
12 | namespace: system
13 | name: webhook-service
14 | path: /convert
15 | conversionReviewVersions:
16 | - v1
17 |
--------------------------------------------------------------------------------
/config/default/manager_config_patch.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: controller-manager
5 | namespace: system
6 | spec:
7 | template:
8 | spec:
9 | containers:
10 | - name: manager
11 | args:
12 | - "--config=controller_manager_config.yaml"
13 | volumeMounts:
14 | - name: manager-config
15 | mountPath: /controller_manager_config.yaml
16 | subPath: controller_manager_config.yaml
17 | volumes:
18 | - name: manager-config
19 | configMap:
20 | name: manager-config
21 |
--------------------------------------------------------------------------------
/config/default/metrics_service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: grafana-operator
6 | app.kubernetes.io/managed-by: olm
7 | name: operator-metrics-service
8 | namespace: system
9 | spec:
10 | ports:
11 | - name: metrics
12 | port: 9090
13 | protocol: TCP
14 | targetPort: metrics
15 | - port: 8888
16 | targetPort: pprof
17 | protocol: TCP
18 | name: pprof
19 | selector:
20 | app.kubernetes.io/name: grafana-operator
21 | app.kubernetes.io/managed-by: olm
22 |
--------------------------------------------------------------------------------
/config/manager/controller_manager_config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1
2 | kind: ControllerManagerConfig
3 | health:
4 | healthProbeBindAddress: :8081
5 | metrics:
6 | bindAddress: 127.0.0.1:8080
7 | webhook:
8 | port: 9443
9 | leaderElection:
10 | leaderElect: true
11 | resourceName: f75f3bba.integreatly.org
12 |
--------------------------------------------------------------------------------
/config/manager/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 |
4 | resources:
5 | - manager.yaml
6 |
7 | generatorOptions:
8 | disableNameSuffixHash: true
9 |
10 | configMapGenerator:
11 | - files:
12 | - controller_manager_config.yaml
13 | name: manager-config
14 |
15 | images:
16 | - name: controller
17 | newName: ghcr.io/grafana/grafana-operator
18 | newTag: v5.8.1
19 |
--------------------------------------------------------------------------------
/config/manifests/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # These resources constitute the fully configured set of manifests
2 | # used to generate the 'manifests/' directory in a bundle.
3 | resources:
4 | - bases/grafana-operator.clusterserviceversion.yaml
5 | - ../default
6 | - ../samples
7 | # [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix.
8 | # Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager.
9 | # These patches remove the unnecessary "cert" volume and its manager container volumeMount.
10 | #patchesJson6902:
11 | #- target:
12 | # group: apps
13 | # version: v1
14 | # kind: Deployment
15 | # name: controller-manager
16 | # namespace: system
17 | # patch: |-
18 | # # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs.
19 | # # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment.
20 | # - op: remove
21 | # path: /spec/template/spec/containers/1/volumeMounts/0
22 | # # Remove the "cert" volume, since OLM will create and mount a set of certs.
23 | # # Update the indices in this path if adding or removing volumes in the manager's Deployment.
24 | # - op: remove
25 | # path: /spec/template/spec/volumes/0
26 |
--------------------------------------------------------------------------------
/config/prometheus/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - monitor.yaml
3 |
--------------------------------------------------------------------------------
/config/prometheus/monitor.yaml:
--------------------------------------------------------------------------------
1 | # Prometheus Monitor Service (Metrics)
2 | apiVersion: monitoring.coreos.com/v1
3 | kind: ServiceMonitor
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: grafana-operator
7 | app.kubernetes.io/managed-by: olm
8 | name: controller-manager-metrics-monitor
9 | namespace: system
10 | spec:
11 | endpoints:
12 | - path: /metrics
13 | port: metrics
14 | interval: 60s
15 | selector:
16 | matchLabels:
17 | app.kubernetes.io/name: grafana-operator
18 | app.kubernetes.io/managed-by: olm
19 |
--------------------------------------------------------------------------------
/config/rbac/grafana_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit grafanas.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafana-editor-role
6 | rules:
7 | - apiGroups:
8 | - grafana.integreatly.org
9 | resources:
10 | - grafanas
11 | verbs:
12 | - create
13 | - delete
14 | - get
15 | - list
16 | - patch
17 | - update
18 | - watch
19 | - apiGroups:
20 | - grafana.integreatly.org
21 | resources:
22 | - grafanas/status
23 | verbs:
24 | - get
25 |
--------------------------------------------------------------------------------
/config/rbac/grafana_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view grafanas.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafana-viewer-role
6 | rules:
7 | - apiGroups:
8 | - grafana.integreatly.org
9 | resources:
10 | - grafanas
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - apiGroups:
16 | - grafana.integreatly.org
17 | resources:
18 | - grafanas/status
19 | verbs:
20 | - get
21 |
--------------------------------------------------------------------------------
/config/rbac/grafanaalertrulegroup_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit grafanaalertrulegroups.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafanaalertrulegroup-editor-role
6 | rules:
7 | - apiGroups:
8 | - grafana.integreatly.org
9 | resources:
10 | - grafanaalertrulegroups
11 | verbs:
12 | - create
13 | - delete
14 | - get
15 | - list
16 | - patch
17 | - update
18 | - watch
19 | - apiGroups:
20 | - grafana.integreatly.org
21 | resources:
22 | - grafanaalertrulegroups/status
23 | verbs:
24 | - get
25 |
--------------------------------------------------------------------------------
/config/rbac/grafanaalertrulegroup_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view grafanaalertrulegroups.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafanaalertrulegroup-viewer-role
6 | rules:
7 | - apiGroups:
8 | - grafana.integreatly.org
9 | resources:
10 | - grafanaalertrulegroups
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - apiGroups:
16 | - grafana.integreatly.org
17 | resources:
18 | - grafanaalertrulegroups/status
19 | verbs:
20 | - get
21 |
--------------------------------------------------------------------------------
/config/rbac/grafanacontactpoint_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit grafanacontactpoints.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: clusterrole
7 | app.kubernetes.io/instance: grafanacontactpoint-editor-role
8 | app.kubernetes.io/component: rbac
9 | app.kubernetes.io/created-by: grafana-operator
10 | app.kubernetes.io/part-of: grafana-operator
11 | app.kubernetes.io/managed-by: kustomize
12 | name: grafanacontactpoint-editor-role
13 | rules:
14 | - apiGroups:
15 | - grafana.integreatly.org
16 | resources:
17 | - grafanacontactpoints
18 | verbs:
19 | - create
20 | - delete
21 | - get
22 | - list
23 | - patch
24 | - update
25 | - watch
26 | - apiGroups:
27 | - grafana.integreatly.org
28 | resources:
29 | - grafanacontactpoints/status
30 | verbs:
31 | - get
32 |
--------------------------------------------------------------------------------
/config/rbac/grafanacontactpoint_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view grafanacontactpoints.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: clusterrole
7 | app.kubernetes.io/instance: grafanacontactpoint-viewer-role
8 | app.kubernetes.io/component: rbac
9 | app.kubernetes.io/created-by: grafana-operator
10 | app.kubernetes.io/part-of: grafana-operator
11 | app.kubernetes.io/managed-by: kustomize
12 | name: grafanacontactpoint-viewer-role
13 | rules:
14 | - apiGroups:
15 | - grafana.integreatly.org
16 | resources:
17 | - grafanacontactpoints
18 | verbs:
19 | - get
20 | - list
21 | - watch
22 | - apiGroups:
23 | - grafana.integreatly.org
24 | resources:
25 | - grafanacontactpoints/status
26 | verbs:
27 | - get
28 |
--------------------------------------------------------------------------------
/config/rbac/grafanadashboard_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit grafanadashboards.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafanadashboard-editor-role
6 | rules:
7 | - apiGroups:
8 | - grafana.integreatly.org
9 | resources:
10 | - grafanadashboards
11 | verbs:
12 | - create
13 | - delete
14 | - get
15 | - list
16 | - patch
17 | - update
18 | - watch
19 | - apiGroups:
20 | - grafana.integreatly.org
21 | resources:
22 | - grafanadashboards/status
23 | verbs:
24 | - get
25 |
--------------------------------------------------------------------------------
/config/rbac/grafanadashboard_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view grafanadashboards.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafanadashboard-viewer-role
6 | rules:
7 | - apiGroups:
8 | - grafana.integreatly.org
9 | resources:
10 | - grafanadashboards
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - apiGroups:
16 | - grafana.integreatly.org
17 | resources:
18 | - grafanadashboards/status
19 | verbs:
20 | - get
21 |
--------------------------------------------------------------------------------
/config/rbac/grafanadatasource_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit grafanadatasources.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafanadatasource-editor-role
6 | rules:
7 | - apiGroups:
8 | - grafana.integreatly.org
9 | resources:
10 | - grafanadatasources
11 | verbs:
12 | - create
13 | - delete
14 | - get
15 | - list
16 | - patch
17 | - update
18 | - watch
19 | - apiGroups:
20 | - grafana.integreatly.org
21 | resources:
22 | - grafanadatasources/status
23 | verbs:
24 | - get
25 |
--------------------------------------------------------------------------------
/config/rbac/grafanadatasource_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view grafanadatasources.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafanadatasource-viewer-role
6 | rules:
7 | - apiGroups:
8 | - grafana.integreatly.org
9 | resources:
10 | - grafanadatasources
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - apiGroups:
16 | - grafana.integreatly.org
17 | resources:
18 | - grafanadatasources/status
19 | verbs:
20 | - get
21 |
--------------------------------------------------------------------------------
/config/rbac/grafanafolder_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit grafanafolders.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafanafolder-editor-role
6 | rules:
7 | - apiGroups:
8 | - grafana.integreatly.org
9 | resources:
10 | - grafanafolders
11 | verbs:
12 | - create
13 | - delete
14 | - get
15 | - list
16 | - patch
17 | - update
18 | - watch
19 | - apiGroups:
20 | - grafana.integreatly.org
21 | resources:
22 | - grafanafolders/status
23 | verbs:
24 | - get
25 |
--------------------------------------------------------------------------------
/config/rbac/grafanafolder_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view grafanafolders.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafanafolder-viewer-role
6 | rules:
7 | - apiGroups:
8 | - grafana.integreatly.org
9 | resources:
10 | - grafanafolders
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - apiGroups:
16 | - grafana.integreatly.org
17 | resources:
18 | - grafanafolders/status
19 | verbs:
20 | - get
21 |
--------------------------------------------------------------------------------
/config/rbac/grafanalibrarypanel_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit grafanalibrarypanels.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafanalibrarypanel-editor-role
6 | rules:
7 | - apiGroups:
8 | - grafana.integreatly.org
9 | resources:
10 | - grafanalibrarypanels
11 | verbs:
12 | - create
13 | - delete
14 | - get
15 | - list
16 | - patch
17 | - update
18 | - watch
19 | - apiGroups:
20 | - grafana.integreatly.org
21 | resources:
22 | - grafanalibrarypanels/status
23 | verbs:
24 | - get
25 |
--------------------------------------------------------------------------------
/config/rbac/grafanalibrarypanel_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view grafanalibrarypanels.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: grafanalibrarypanel-viewer-role
6 | rules:
7 | - apiGroups:
8 | - grafana.integreatly.org
9 | resources:
10 | - grafanalibrarypanels
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - apiGroups:
16 | - grafana.integreatly.org
17 | resources:
18 | - grafanalibrarypanels/status
19 | verbs:
20 | - get
21 |
--------------------------------------------------------------------------------
/config/rbac/grafananotificationpolicyroute_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit grafananotificationpolicyroutes.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: clusterrole
7 | app.kubernetes.io/instance: grafananotificationpolicyroute-editor-role
8 | app.kubernetes.io/component: rbac
9 | app.kubernetes.io/created-by: grafana-operator
10 | app.kubernetes.io/part-of: grafana-operator
11 | app.kubernetes.io/managed-by: kustomize
12 | name: grafananotificationpolicyroute-editor-role
13 | rules:
14 | - apiGroups:
15 | - grafana.integreatly.org
16 | resources:
17 | - grafananotificationpolicyroutes
18 | verbs:
19 | - create
20 | - delete
21 | - get
22 | - list
23 | - patch
24 | - update
25 | - watch
26 | - apiGroups:
27 | - grafana.integreatly.org
28 | resources:
29 | - grafananotificationpolicyroutes/status
30 | verbs:
31 | - get
32 |
--------------------------------------------------------------------------------
/config/rbac/grafananotificationpolicyroute_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view grafananotificationpolicyroutes.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: clusterrole
7 | app.kubernetes.io/instance: grafananotificationpolicyroute-viewer-role
8 | app.kubernetes.io/component: rbac
9 | app.kubernetes.io/created-by: grafana-operator
10 | app.kubernetes.io/part-of: grafana-operator
11 | app.kubernetes.io/managed-by: kustomize
12 | name: grafananotificationpolicyroute-viewer-role
13 | rules:
14 | - apiGroups:
15 | - grafana.integreatly.org
16 | resources:
17 | - grafananotificationpolicyroutes
18 | verbs:
19 | - get
20 | - list
21 | - watch
22 | - apiGroups:
23 | - grafana.integreatly.org
24 | resources:
25 | - grafananotificationpolicyroutes/status
26 | verbs:
27 | - get
28 |
--------------------------------------------------------------------------------
/config/rbac/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | # All RBAC will be applied under this service account in
3 | # the deployment namespace. You may comment out this resource
4 | # if your manager will use a service account that exists at
5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding
6 | # subjects if changing service account names.
7 | - service_account.yaml
8 | - role.yaml
9 | - role_binding.yaml
10 | - leader_election_role.yaml
11 | - leader_election_role_binding.yaml
12 |
--------------------------------------------------------------------------------
/config/rbac/leader_election_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions to do leader election.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: Role
4 | metadata:
5 | name: leader-election-role
6 | rules:
7 | - apiGroups:
8 | - ""
9 | resources:
10 | - configmaps
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - create
16 | - update
17 | - patch
18 | - delete
19 | - apiGroups:
20 | - coordination.k8s.io
21 | resources:
22 | - leases
23 | verbs:
24 | - get
25 | - list
26 | - watch
27 | - create
28 | - update
29 | - patch
30 | - delete
31 | - apiGroups:
32 | - ""
33 | resources:
34 | - events
35 | verbs:
36 | - create
37 | - patch
38 |
--------------------------------------------------------------------------------
/config/rbac/leader_election_role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: RoleBinding
3 | metadata:
4 | name: leader-election-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: Role
8 | name: leader-election-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: controller-manager
12 | namespace: system
13 |
--------------------------------------------------------------------------------
/config/rbac/role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: manager-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: ClusterRole
8 | name: manager-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: controller-manager
12 | namespace: system
13 |
--------------------------------------------------------------------------------
/config/rbac/service_account.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | name: controller-manager
5 | namespace: system
6 |
--------------------------------------------------------------------------------
/config/samples/grafana_v1beta1_grafana.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana-a
6 | labels:
7 | dashboards: "grafana-a"
8 | folders: "grafana-a"
9 | spec:
10 | config:
11 | security:
12 | admin_user: root
13 | admin_password: start
14 | log:
15 | mode: "console"
16 | auth:
17 | disable_login_form: "false"
18 |
--------------------------------------------------------------------------------
/config/samples/grafana_v1beta1_grafanacontactpoint.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaContactPoint
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: grafanacontactpoint
6 | app.kubernetes.io/instance: grafanacontactpoint-sample
7 | app.kubernetes.io/part-of: grafana-operator
8 | app.kubernetes.io/managed-by: kustomize
9 | app.kubernetes.io/created-by: grafana-operator
10 | name: grafanacontactpoint-sample
11 | spec:
12 | name: grafanacontactpoint-sample
13 | type: "email"
14 | instanceSelector:
15 | matchLabels:
16 | dashboards: "grafana-a"
17 | settings:
18 | email:
19 |
--------------------------------------------------------------------------------
/config/samples/grafana_v1beta1_grafanadashboard.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: GrafanaDashboard
4 | metadata:
5 | name: grafanadashboard-sample
6 | spec:
7 | instanceSelector:
8 | matchLabels:
9 | dashboards: "grafana-a"
10 | json: >
11 | {
12 | "id": null,
13 | "title": "Simple Dashboard",
14 | "tags": [],
15 | "style": "dark",
16 | "timezone": "browser",
17 | "editable": true,
18 | "hideControls": false,
19 | "graphTooltip": 1,
20 | "panels": [],
21 | "time": {
22 | "from": "now-6h",
23 | "to": "now"
24 | },
25 | "timepicker": {
26 | "time_options": [],
27 | "refresh_intervals": []
28 | },
29 | "templating": {
30 | "list": []
31 | },
32 | "annotations": {
33 | "list": []
34 | },
35 | "refresh": "5s",
36 | "schemaVersion": 17,
37 | "version": 0,
38 | "links": []
39 | }
40 |
--------------------------------------------------------------------------------
/config/samples/grafana_v1beta1_grafanadatasource.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDatasource
3 | metadata:
4 | name: grafanadatasource-sample
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | dashboards: "grafana-a"
9 | plugins:
10 | - name: grafana-clock-panel
11 | version: 1.3.0
12 | datasource:
13 | name: prometheus
14 | type: prometheus
15 | access: proxy
16 | url: http://prometheus-service:9090
17 | isDefault: true
18 | jsonData:
19 | "tlsSkipVerify": true
20 | "timeInterval": "5s"
21 |
--------------------------------------------------------------------------------
/config/samples/grafana_v1beta1_grafanafolder.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaFolder
3 | metadata:
4 | name: grafanafolder-sample
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | dashboards: "grafana-a"
9 | title: "Example Folder"
10 |
--------------------------------------------------------------------------------
/config/samples/grafana_v1beta1_grafanalibrarypanel.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaLibraryPanel
3 | metadata:
4 | name: grafana-library-panel-inline-envs
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | dashboards: "grafana"
9 | envs:
10 | - name: CUSTOM_RANGE_ENV
11 | value: "now - 12h"
12 | plugins:
13 | - name: grafana-piechart-panel
14 | version: 1.3.9
15 | jsonnet: >
16 | local myRange = std.extVar('CUSTOM_RANGE_ENV');
17 | {
18 | model: {}
19 | }
20 |
--------------------------------------------------------------------------------
/config/samples/grafana_v1beta1_grafanamutetiming.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaMuteTiming
3 | metadata:
4 | name: mutetiming-sample
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | dashboards: "grafana"
9 | name: mutetiming-sample
10 | editable: false
11 | time_intervals:
12 | - times:
13 | - start_time: "00:00"
14 | end_time: "06:00"
15 | weekdays: [saturday]
16 | days_of_month: ["1", "15"]
17 | location: Asia/Shanghai
18 |
--------------------------------------------------------------------------------
/config/samples/grafana_v1beta1_grafananotificationpolicy.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaNotificationPolicy
3 | metadata:
4 | name: grafananotificationpolicy-sample
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | dashboards: "grafana"
9 | route:
10 | receiver: Grafana Cloud OnCall
11 | group_by:
12 | - grafana_folder
13 | - alertname
14 | routes:
15 | - receiver: grafana-default-email
16 | object_matchers:
17 | - - foo
18 | - =
19 | - bar
20 | routes:
21 | - receiver: Grafana Cloud OnCall
22 | object_matchers:
23 | - - severity
24 | - =
25 | - critical
26 |
--------------------------------------------------------------------------------
/config/samples/grafana_v1beta1_grafananotificationpolicyroute.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaNotificationPolicyRoute
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: grafananotificationpolicyroute
6 | app.kubernetes.io/instance: grafananotificationpolicyroute-sample
7 | app.kubernetes.io/part-of: grafana-operator
8 | app.kubernetes.io/managed-by: kustomize
9 | app.kubernetes.io/created-by: grafana-operator
10 | name: grafananotificationpolicyroute-sample
11 | spec:
12 | # TODO(user): Add fields here
13 |
--------------------------------------------------------------------------------
/config/samples/grafana_v1beta1_grafananotificationtemplate.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: GrafanaNotificationTemplate
4 | metadata:
5 | name: test
6 | spec:
7 | instanceSelector:
8 | matchLabels:
9 | dashboards: "grafana"
10 | name: test
11 | template: |
12 | {{ define "SlackAlert" }}
13 | [{{.Status}}] {{ .Labels.alertname }}
14 | {{ .Annotations.AlertValues }}
15 | {{ end }}
16 |
17 | {{ define "SlackAlertMessage" }}
18 | {{ if gt (len .Alerts.Firing) 0 }}
19 | {{ len .Alerts.Firing }} firing:
20 | {{ range .Alerts.Firing }} {{ template "SlackAlert" . }} {{ end }}
21 | {{ end }}
22 | {{ if gt (len .Alerts.Resolved) 0 }}
23 | {{ len .Alerts.Resolved }} resolved:
24 | {{ range .Alerts.Resolved }} {{ template "SlackAlert" . }} {{ end }}
25 | {{ end }}
26 | {{ end }}
27 |
28 | {{ template "SlackAlertMessage" . }}
29 |
--------------------------------------------------------------------------------
/config/samples/kustomization.yaml:
--------------------------------------------------------------------------------
1 | ## Append samples you want in your CSV to this file as resources ##
2 | resources:
3 | - grafana_v1beta1_grafana.yaml
4 | - grafana_v1beta1_grafanadashboard.yaml
5 | - grafana_v1beta1_grafanadatasource.yaml
6 | - grafana_v1beta1_grafanafolder.yaml
7 | - grafana_v1beta1_grafanaalertrulegroup.yaml
8 | - grafana_v1beta1_grafanacontactpoint.yaml
9 | - grafana_v1beta1_grafananotificationpolicy.yaml
10 | - grafana_v1beta1_grafananotificationtemplate.yaml
11 | - grafana_v1beta1_grafananotificationpolicyroute.yaml
12 | - grafana_v1beta1_grafanalibrarypanel.yaml
13 | - grafana_v1beta1_grafanamutetiming.yaml
14 | #+kubebuilder:scaffold:manifestskustomizesamples
15 |
--------------------------------------------------------------------------------
/config/scorecard/bases/config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: scorecard.operatorframework.io/v1alpha3
2 | kind: Configuration
3 | metadata:
4 | name: config
5 | stages:
6 | - parallel: true
7 | tests: []
8 |
--------------------------------------------------------------------------------
/config/scorecard/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - bases/config.yaml
3 | patches:
4 | - path: patches/olm.config.yaml
5 | target:
6 | group: scorecard.operatorframework.io
7 | kind: Configuration
8 | name: config
9 | version: v1alpha3
10 |
--------------------------------------------------------------------------------
/config/scorecard/patches/basic.config.yaml:
--------------------------------------------------------------------------------
1 | - op: add
2 | path: /stages/0/tests/-
3 | value:
4 | entrypoint:
5 | - scorecard-test
6 | - basic-check-spec
7 | image: quay.io/operator-framework/scorecard-test:v1.15.0
8 | labels:
9 | suite: basic
10 | test: basic-check-spec-test
11 |
--------------------------------------------------------------------------------
/config/scorecard/patches/olm.config.yaml:
--------------------------------------------------------------------------------
1 | - op: add
2 | path: /stages/0/tests/-
3 | value:
4 | entrypoint:
5 | - scorecard-test
6 | - olm-bundle-validation
7 | image: quay.io/operator-framework/scorecard-test:v1.15.0
8 | labels:
9 | suite: olm
10 | test: olm-bundle-validation-test
11 | - op: add
12 | path: /stages/0/tests/-
13 | value:
14 | entrypoint:
15 | - scorecard-test
16 | - olm-crds-have-validation
17 | image: quay.io/operator-framework/scorecard-test:v1.15.0
18 | labels:
19 | suite: olm
20 | test: olm-crds-have-validation-test
21 | - op: add
22 | path: /stages/0/tests/-
23 | value:
24 | entrypoint:
25 | - scorecard-test
26 | - olm-crds-have-resources
27 | image: quay.io/operator-framework/scorecard-test:v1.15.0
28 | labels:
29 | suite: olm
30 | test: olm-crds-have-resources-test
31 | - op: add
32 | path: /stages/0/tests/-
33 | value:
34 | entrypoint:
35 | - scorecard-test
36 | - olm-spec-descriptors
37 | image: quay.io/operator-framework/scorecard-test:v1.15.0
38 | labels:
39 | suite: olm
40 | test: olm-spec-descriptors-test
41 | - op: add
42 | path: /stages/0/tests/-
43 | value:
44 | entrypoint:
45 | - scorecard-test
46 | - olm-status-descriptors
47 | image: quay.io/operator-framework/scorecard-test:v1.15.0
48 | labels:
49 | suite: olm
50 | test: olm-status-descriptors-test
51 |
--------------------------------------------------------------------------------
/controllers/autodetect/main.go:
--------------------------------------------------------------------------------
1 | // Package autodetect is for auto-detecting traits from the environment (platform, APIs, ...).
2 | package autodetect
3 |
4 | import (
5 | "k8s.io/client-go/discovery"
6 | "k8s.io/client-go/rest"
7 | )
8 |
9 | var _ AutoDetect = (*autoDetect)(nil)
10 |
11 | // AutoDetect provides an assortment of routines that auto-detect traits based on the runtime.
12 | type AutoDetect interface {
13 | IsOpenshift() (bool, error)
14 | }
15 |
16 | type autoDetect struct {
17 | dcl discovery.DiscoveryInterface
18 | }
19 |
20 | // New creates a new auto-detection worker, using the given client when talking to the current cluster.
21 | func New(restConfig *rest.Config) (AutoDetect, error) {
22 | dcl, err := discovery.NewDiscoveryClientForConfig(restConfig)
23 | if err != nil {
24 | // it's pretty much impossible to get into this problem, as most of the
25 | // code branches from the previous call just won't fail at all,
26 | // but let's handle this error anyway...
27 | return nil, err
28 | }
29 |
30 | return &autoDetect{
31 | dcl: dcl,
32 | }, nil
33 | }
34 |
35 | // Platform returns the detected platform this operator is running on. Possible values: Kubernetes, OpenShift.
36 | func (a *autoDetect) IsOpenshift() (bool, error) {
37 | apiList, err := a.dcl.ServerGroups()
38 | if err != nil {
39 | return false, err
40 | }
41 |
42 | apiGroups := apiList.Groups
43 | for i := range apiGroups {
44 | if apiGroups[i].Name == "route.openshift.io" {
45 | return true, nil
46 | }
47 | }
48 |
49 | return false, nil
50 | }
51 |
--------------------------------------------------------------------------------
/controllers/autodetect/main_test.go:
--------------------------------------------------------------------------------
1 | package autodetect_test
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 | "net/http/httptest"
7 | "testing"
8 |
9 | "github.com/grafana/grafana-operator/v5/controllers/autodetect"
10 | "github.com/stretchr/testify/assert"
11 | "github.com/stretchr/testify/require"
12 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13 | "k8s.io/client-go/rest"
14 | )
15 |
16 | func TestDetectPlatformBasedOnAvailableAPIGroups(t *testing.T) {
17 | for _, tt := range []struct {
18 | apiGroupList *metav1.APIGroupList
19 | expected bool
20 | }{
21 | {
22 | &metav1.APIGroupList{},
23 | false,
24 | },
25 | {
26 | &metav1.APIGroupList{
27 | Groups: []metav1.APIGroup{
28 | {
29 | Name: "route.openshift.io",
30 | },
31 | },
32 | },
33 | true,
34 | },
35 | } {
36 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
37 | output, err := json.Marshal(tt.apiGroupList)
38 | require.NoError(t, err)
39 |
40 | w.Header().Set("Content-Type", "application/json")
41 | w.WriteHeader(http.StatusOK)
42 | _, err = w.Write(output)
43 | require.NoError(t, err)
44 | }))
45 | defer server.Close()
46 |
47 | autoDetect, err := autodetect.New(&rest.Config{Host: server.URL})
48 | require.NoError(t, err)
49 |
50 | // test
51 | plt, err := autoDetect.IsOpenshift()
52 |
53 | // verify
54 | assert.NoError(t, err)
55 | assert.Equal(t, tt.expected, plt)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/controllers/client/common.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 |
8 | v1 "k8s.io/api/core/v1"
9 | "sigs.k8s.io/controller-runtime/pkg/client"
10 | )
11 |
12 | func GetValueFromSecretKey(ctx context.Context, ref *v1.SecretKeySelector, c client.Client, namespace string) ([]byte, error) {
13 | if ref == nil {
14 | return nil, errors.New("empty secret key selector")
15 | }
16 |
17 | secret := &v1.Secret{}
18 | selector := client.ObjectKey{
19 | Name: ref.Name,
20 | Namespace: namespace,
21 | }
22 | err := c.Get(ctx, selector, secret)
23 | if err != nil {
24 | return nil, err
25 | }
26 |
27 | if secret.Data == nil {
28 | return nil, fmt.Errorf("empty credential secret: %v/%v", namespace, ref.Name)
29 | }
30 |
31 | if val, ok := secret.Data[ref.Key]; ok {
32 | return val, nil
33 | }
34 |
35 | return nil, fmt.Errorf("credentials not found in secret: %v/%v", namespace, ref.Name)
36 | }
37 |
--------------------------------------------------------------------------------
/controllers/client/http_client.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "context"
5 | "net/http"
6 | "time"
7 |
8 | "github.com/grafana/grafana-operator/v5/api/v1beta1"
9 | "github.com/grafana/grafana-operator/v5/controllers/metrics"
10 | "github.com/prometheus/client_golang/prometheus"
11 | "sigs.k8s.io/controller-runtime/pkg/client"
12 | )
13 |
14 | func NewHTTPClient(ctx context.Context, c client.Client, grafana *v1beta1.Grafana) (*http.Client, error) {
15 | var timeout time.Duration
16 | if grafana.Spec.Client != nil && grafana.Spec.Client.TimeoutSeconds != nil {
17 | timeout = max(time.Duration(*grafana.Spec.Client.TimeoutSeconds), 0)
18 | } else {
19 | timeout = 10
20 | }
21 |
22 | tlsConfig, err := buildTLSConfiguration(ctx, c, grafana)
23 | if err != nil {
24 | return nil, err
25 | }
26 |
27 | transport := NewInstrumentedRoundTripper(grafana.IsExternal(), tlsConfig, metrics.GrafanaAPIRequests.MustCurryWith(prometheus.Labels{
28 | "instance_namespace": grafana.Namespace,
29 | "instance_name": grafana.Name,
30 | }))
31 | if grafana.Spec.Client != nil && grafana.Spec.Client.Headers != nil {
32 | transport.(*instrumentedRoundTripper).addHeaders(grafana.Spec.Client.Headers) //nolint:errcheck
33 | }
34 |
35 | return &http.Client{
36 | Transport: transport,
37 | Timeout: time.Second * timeout,
38 | }, nil
39 | }
40 |
--------------------------------------------------------------------------------
/controllers/content/fetchers/configmap_fetcher.go:
--------------------------------------------------------------------------------
1 | package fetchers
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "github.com/grafana/grafana-operator/v5/api/v1beta1"
8 | v1 "k8s.io/api/core/v1"
9 | "sigs.k8s.io/controller-runtime/pkg/client"
10 | )
11 |
12 | func FetchDashboardFromConfigMap(cr v1beta1.GrafanaContentResource, c client.Client) ([]byte, error) {
13 | spec := cr.GrafanaContentSpec()
14 | if spec == nil {
15 | return nil, nil // TODO
16 | }
17 | ref := spec.ConfigMapRef
18 | dashboardConfigMap := &v1.ConfigMap{}
19 | selector := client.ObjectKey{
20 | Namespace: cr.GetNamespace(),
21 | Name: ref.Name,
22 | }
23 |
24 | err := c.Get(context.Background(), selector, dashboardConfigMap)
25 | if err != nil {
26 | return nil, err
27 | }
28 |
29 | if content, ok := dashboardConfigMap.Data[ref.Key]; ok {
30 | return []byte(content), nil
31 | }
32 |
33 | return nil, fmt.Errorf("cannot find key '%v' in config map '%v' for dashboard %v/%v",
34 | ref.Key, ref.Name, cr.GetNamespace(), cr.GetName())
35 | }
36 |
--------------------------------------------------------------------------------
/controllers/content/fetchers/grafana_com_fetcher_test.go:
--------------------------------------------------------------------------------
1 | package fetchers
2 |
3 | import (
4 | "context"
5 | "testing"
6 |
7 | "github.com/grafana/grafana-operator/v5/api/v1beta1"
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | func TestFetchDashboardFromGrafanaCom(t *testing.T) {
12 | dashboard := &v1beta1.GrafanaDashboard{
13 | Spec: v1beta1.GrafanaDashboardSpec{
14 | GrafanaContentSpec: v1beta1.GrafanaContentSpec{
15 | GrafanaCom: &v1beta1.GrafanaComContentReference{
16 | ID: 1860,
17 | },
18 | },
19 | },
20 | Status: v1beta1.GrafanaDashboardStatus{},
21 | }
22 |
23 | fetchedDashboard, err := FetchFromGrafanaCom(context.Background(), dashboard, k8sClient)
24 | assert.Nil(t, err)
25 | assert.NotNil(t, fetchedDashboard, "Fetched dashboard shouldn't be empty")
26 | assert.GreaterOrEqual(t, *dashboard.Spec.GrafanaCom.Revision, 30, "At least 30 revisions exist for dashboard 1860 as of 2023-03-29")
27 |
28 | assert.False(t, dashboard.Status.ContentTimestamp.Time.IsZero(), "ContentTimestamp should have been set")
29 | assert.NotEmpty(t, dashboard.Status.ContentURL, "ContentURL should have been set")
30 | }
31 |
--------------------------------------------------------------------------------
/controllers/content/fetchers/suite_test.go:
--------------------------------------------------------------------------------
1 | package fetchers
2 |
3 | import (
4 | "testing"
5 |
6 | grafanav1beta1 "github.com/grafana/grafana-operator/v5/api/v1beta1"
7 |
8 | "k8s.io/client-go/kubernetes/scheme"
9 | "k8s.io/client-go/rest"
10 | "sigs.k8s.io/controller-runtime/pkg/client"
11 | "sigs.k8s.io/controller-runtime/pkg/envtest"
12 | logf "sigs.k8s.io/controller-runtime/pkg/log"
13 | "sigs.k8s.io/controller-runtime/pkg/log/zap"
14 |
15 | . "github.com/onsi/ginkgo/v2"
16 | . "github.com/onsi/gomega"
17 | )
18 |
19 | var (
20 | cfg *rest.Config
21 | k8sClient client.Client
22 | testEnv *envtest.Environment
23 | )
24 |
25 | func TestAPIs(t *testing.T) {
26 | RegisterFailHandler(Fail)
27 |
28 | RunSpecs(t, "Fetchers Suite")
29 | }
30 |
31 | var _ = BeforeSuite(func() {
32 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
33 |
34 | By("bootstrapping test environment")
35 | testEnv = &envtest.Environment{}
36 |
37 | cfg, err := testEnv.Start()
38 | Expect(err).NotTo(HaveOccurred())
39 | Expect(cfg).NotTo(BeNil())
40 |
41 | err = grafanav1beta1.AddToScheme(scheme.Scheme)
42 | Expect(err).NotTo(HaveOccurred())
43 |
44 | //+kubebuilder:scaffold:scheme
45 |
46 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
47 | Expect(err).NotTo(HaveOccurred())
48 | Expect(k8sClient).NotTo(BeNil())
49 | })
50 |
51 | var _ = AfterSuite(func() {
52 | By("tearing down the test environment")
53 | err := testEnv.Stop()
54 | Expect(err).NotTo(HaveOccurred())
55 | })
56 |
--------------------------------------------------------------------------------
/controllers/folder_controller_test.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/grafana/grafana-operator/v5/api/v1beta1"
7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8 |
9 | . "github.com/onsi/ginkgo/v2"
10 | . "github.com/onsi/gomega"
11 | )
12 |
13 | var _ = Describe("Folder: Reconciler", func() {
14 | It("Results in NoMatchingInstances Condition", func() {
15 | // Create object
16 | cr := &v1beta1.GrafanaFolder{
17 | ObjectMeta: metav1.ObjectMeta{
18 | Name: "no-match",
19 | Namespace: "default",
20 | },
21 | Spec: v1beta1.GrafanaFolderSpec{
22 | GrafanaCommonSpec: instanceSelectorNoMatchingInstances,
23 | },
24 | }
25 | ctx := context.Background()
26 | err := k8sClient.Create(ctx, cr)
27 | Expect(err).ToNot(HaveOccurred())
28 |
29 | // Reconciliation Request
30 | req := requestFromMeta(cr.ObjectMeta)
31 |
32 | // Reconcile
33 | r := GrafanaFolderReconciler{Client: k8sClient}
34 | _, err = r.Reconcile(ctx, req)
35 | Expect(err).ShouldNot(HaveOccurred()) // NoMatchingInstances is a valid reconciliation result
36 |
37 | resultCr := &v1beta1.GrafanaFolder{}
38 | Expect(r.Get(ctx, req.NamespacedName, resultCr)).Should(Succeed()) // NoMatchingInstances is a valid status
39 |
40 | // Verify NoMatchingInstances condition
41 | Expect(resultCr.Status.Conditions).Should(ContainElement(HaveField("Type", conditionNoMatchingInstance)))
42 | })
43 | })
44 |
--------------------------------------------------------------------------------
/controllers/librarypanel_controller_test.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/grafana/grafana-operator/v5/api/v1beta1"
7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8 |
9 | . "github.com/onsi/ginkgo/v2"
10 | . "github.com/onsi/gomega"
11 | )
12 |
13 | var _ = Describe("LibraryPanel: Reconciler", func() {
14 | It("Results in NoMatchingInstances Condition", func() {
15 | // Create object
16 | cr := &v1beta1.GrafanaLibraryPanel{
17 | ObjectMeta: metav1.ObjectMeta{
18 | Name: "no-match",
19 | Namespace: "default",
20 | },
21 | Spec: v1beta1.GrafanaLibraryPanelSpec{
22 | GrafanaCommonSpec: instanceSelectorNoMatchingInstances,
23 | GrafanaContentSpec: v1beta1.GrafanaContentSpec{JSON: "{}"},
24 | },
25 | }
26 | ctx := context.Background()
27 | err := k8sClient.Create(ctx, cr)
28 | Expect(err).ToNot(HaveOccurred())
29 |
30 | // Reconciliation Request
31 | req := requestFromMeta(cr.ObjectMeta)
32 |
33 | // Reconcile
34 | r := GrafanaLibraryPanelReconciler{Client: k8sClient}
35 | _, err = r.Reconcile(ctx, req)
36 | Expect(err).ShouldNot(HaveOccurred()) // NoMatchingInstances is a valid reconciliation result
37 |
38 | resultCr := &v1beta1.GrafanaLibraryPanel{}
39 | Expect(r.Get(ctx, req.NamespacedName, resultCr)).Should(Succeed()) // NoMatchingInstances is a valid status
40 |
41 | // Verify NoMatchingInstances condition
42 | Expect(resultCr.Status.Conditions).Should(ContainElement(HaveField("Type", conditionNoMatchingInstance)))
43 | })
44 | })
45 |
--------------------------------------------------------------------------------
/controllers/model/dashboard_resources.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "fmt"
5 |
6 | grafanav1beta1 "github.com/grafana/grafana-operator/v5/api/v1beta1"
7 | v1 "k8s.io/api/core/v1"
8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9 | "k8s.io/apimachinery/pkg/runtime"
10 | "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
11 | )
12 |
13 | func GetPluginsConfigMap(cr *grafanav1beta1.Grafana, scheme *runtime.Scheme) *v1.ConfigMap {
14 | config := &v1.ConfigMap{
15 | ObjectMeta: metav1.ObjectMeta{
16 | Name: fmt.Sprintf("%s-plugins", cr.Name),
17 | Namespace: cr.Namespace,
18 | Labels: GetCommonLabels(),
19 | },
20 | }
21 | controllerutil.SetControllerReference(cr, config, scheme) //nolint:errcheck
22 | return config
23 | }
24 |
--------------------------------------------------------------------------------
/controllers/model/utils.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "crypto/rand"
5 | "encoding/base64"
6 | "maps"
7 |
8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9 | )
10 |
11 | func generateRandomBytes(n int) []byte {
12 | b := make([]byte, n)
13 | _, err := rand.Read(b)
14 | if err != nil {
15 | panic(err)
16 | }
17 | return b
18 | }
19 |
20 | func RandStringRunes(s int) string {
21 | b := generateRandomBytes(s)
22 | return base64.URLEncoding.EncodeToString(b)
23 | }
24 |
25 | func MergeAnnotations(requested map[string]string, existing map[string]string) map[string]string {
26 | if existing == nil {
27 | return requested
28 | }
29 |
30 | maps.Copy(existing, requested)
31 | return existing
32 | }
33 |
34 | func BoolPtr(b bool) *bool { return &b }
35 |
36 | func IntPtr(b int64) *int64 { return &b }
37 |
38 | func SetInheritedLabels(obj metav1.ObjectMetaAccessor, extraLabels map[string]string) {
39 | meta := obj.GetObjectMeta()
40 | labels := meta.GetLabels()
41 | if labels == nil {
42 | labels = make(map[string]string)
43 | }
44 | // Inherit labels from the parent grafana instance if any
45 | maps.Copy(labels, extraLabels)
46 | // Ensure default CommonLabels for child resources
47 | maps.Copy(labels, GetCommonLabels())
48 | meta.SetLabels(labels)
49 | }
50 |
--------------------------------------------------------------------------------
/controllers/notificationtemplate_controller_test.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/grafana/grafana-operator/v5/api/v1beta1"
7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8 |
9 | . "github.com/onsi/ginkgo/v2"
10 | . "github.com/onsi/gomega"
11 | )
12 |
13 | var _ = Describe("NotificationTemplate: Reconciler", func() {
14 | It("Results in NoMatchingInstances Condition", func() {
15 | // Create object
16 | cr := &v1beta1.GrafanaNotificationTemplate{
17 | ObjectMeta: metav1.ObjectMeta{
18 | Name: "no-match",
19 | Namespace: "default",
20 | },
21 | Spec: v1beta1.GrafanaNotificationTemplateSpec{
22 | GrafanaCommonSpec: instanceSelectorNoMatchingInstances,
23 | Name: "NoMatch",
24 | },
25 | }
26 | ctx := context.Background()
27 | err := k8sClient.Create(ctx, cr)
28 | Expect(err).ToNot(HaveOccurred())
29 |
30 | // Reconciliation Request
31 | req := requestFromMeta(cr.ObjectMeta)
32 |
33 | // Reconcile
34 | r := GrafanaNotificationTemplateReconciler{Client: k8sClient}
35 | _, err = r.Reconcile(ctx, req)
36 | Expect(err).ShouldNot(HaveOccurred()) // NoMatchingInstances is a valid reconciliation result
37 |
38 | resultCr := &v1beta1.GrafanaNotificationTemplate{}
39 | Expect(r.Get(ctx, req.NamespacedName, resultCr)).Should(Succeed()) // NoMatchingInstances is a valid status
40 |
41 | // Verify NoMatchingInstances condition
42 | Expect(resultCr.Status.Conditions).Should(ContainElement(HaveField("Type", conditionNoMatchingInstance)))
43 | })
44 | })
45 |
--------------------------------------------------------------------------------
/controllers/reconcilers/grafana/admin_secret_reconciler_test.go:
--------------------------------------------------------------------------------
1 | package grafana
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/grafana/grafana-operator/v5/api/v1beta1"
7 | . "github.com/onsi/ginkgo/v2"
8 | . "github.com/onsi/gomega"
9 | "k8s.io/client-go/kubernetes/scheme"
10 | )
11 |
12 | var _ = Describe("Reconcile AdminSecret", func() {
13 | It("runs successfully with disabled default admin secret", func() {
14 | r := NewAdminSecretReconciler(k8sClient)
15 | cr := &v1beta1.Grafana{
16 | Spec: v1beta1.GrafanaSpec{
17 | DisableDefaultAdminSecret: true,
18 | },
19 | }
20 |
21 | vars := &v1beta1.OperatorReconcileVars{}
22 | status, err := r.Reconcile(context.Background(), cr, vars, scheme.Scheme)
23 |
24 | Expect(err).ToNot(HaveOccurred())
25 | Expect(status).To(Equal(v1beta1.OperatorStageResultSuccess))
26 | })
27 | })
28 |
--------------------------------------------------------------------------------
/controllers/reconcilers/grafana/deployment_reconciler_test.go:
--------------------------------------------------------------------------------
1 | package grafana
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 |
7 | "github.com/grafana/grafana-operator/v5/api/v1beta1"
8 | config2 "github.com/grafana/grafana-operator/v5/controllers/config"
9 |
10 | "github.com/stretchr/testify/assert"
11 | )
12 |
13 | func Test_getGrafanaImage(t *testing.T) {
14 | cr := &v1beta1.Grafana{
15 | Spec: v1beta1.GrafanaSpec{
16 | Version: "",
17 | },
18 | }
19 |
20 | expectedDeploymentImage := fmt.Sprintf("%s:%s", config2.GrafanaImage, config2.GrafanaVersion)
21 |
22 | assert.Equal(t, expectedDeploymentImage, getGrafanaImage(cr))
23 | }
24 |
25 | func Test_getGrafanaImage_specificVersion(t *testing.T) {
26 | cr := &v1beta1.Grafana{
27 | Spec: v1beta1.GrafanaSpec{
28 | Version: "10.4.0",
29 | },
30 | }
31 |
32 | expectedDeploymentImage := fmt.Sprintf("%s:10.4.0", config2.GrafanaImage)
33 |
34 | assert.Equal(t, expectedDeploymentImage, getGrafanaImage(cr))
35 | }
36 |
37 | func Test_getGrafanaImage_withImageInVersion(t *testing.T) {
38 | expectedDeploymentImage := "docker.io/grafana/grafana@sha256:b7fcb534f7b3512801bb3f4e658238846435804deb479d105b5cdc680847c272"
39 | cr := &v1beta1.Grafana{
40 | Spec: v1beta1.GrafanaSpec{
41 | Version: expectedDeploymentImage,
42 | },
43 | }
44 |
45 | assert.Equal(t, expectedDeploymentImage, getGrafanaImage(cr))
46 | }
47 |
--------------------------------------------------------------------------------
/controllers/reconcilers/grafana/service_account_reconciler.go:
--------------------------------------------------------------------------------
1 | package grafana
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/grafana/grafana-operator/v5/api/v1beta1"
7 | "github.com/grafana/grafana-operator/v5/controllers/model"
8 | "github.com/grafana/grafana-operator/v5/controllers/reconcilers"
9 | "k8s.io/apimachinery/pkg/runtime"
10 | "sigs.k8s.io/controller-runtime/pkg/client"
11 | "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
12 | )
13 |
14 | type ServiceAccountReconciler struct {
15 | client client.Client
16 | }
17 |
18 | func NewServiceAccountReconciler(client client.Client) reconcilers.OperatorGrafanaReconciler {
19 | return &ServiceAccountReconciler{
20 | client: client,
21 | }
22 | }
23 |
24 | func (r *ServiceAccountReconciler) Reconcile(ctx context.Context, cr *v1beta1.Grafana, vars *v1beta1.OperatorReconcileVars, scheme *runtime.Scheme) (v1beta1.OperatorStageStatus, error) {
25 | sa := model.GetGrafanaServiceAccount(cr, scheme)
26 |
27 | _, err := controllerutil.CreateOrUpdate(ctx, r.client, sa, func() error {
28 | err := v1beta1.Merge(sa, cr.Spec.ServiceAccount)
29 | if err != nil {
30 | return err
31 | }
32 |
33 | if scheme != nil {
34 | err = controllerutil.SetControllerReference(cr, sa, scheme)
35 | if err != nil {
36 | return err
37 | }
38 | }
39 |
40 | model.SetInheritedLabels(sa, cr.Labels)
41 |
42 | return nil
43 | })
44 | if err != nil {
45 | return v1beta1.OperatorStageResultFailed, err
46 | }
47 |
48 | return v1beta1.OperatorStageResultSuccess, nil
49 | }
50 |
--------------------------------------------------------------------------------
/controllers/reconcilers/reconciler.go:
--------------------------------------------------------------------------------
1 | package reconcilers
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/grafana/grafana-operator/v5/api/v1beta1"
7 | "k8s.io/apimachinery/pkg/runtime"
8 | )
9 |
10 | type OperatorGrafanaReconciler interface {
11 | Reconcile(ctx context.Context, cr *v1beta1.Grafana, vars *v1beta1.OperatorReconcileVars, scheme *runtime.Scheme) (v1beta1.OperatorStageStatus, error)
12 | }
13 |
--------------------------------------------------------------------------------
/deploy/helm/cr.yaml:
--------------------------------------------------------------------------------
1 | git-repo: helm-charts
2 | owner: grafana
3 | skip-existing: true
4 |
--------------------------------------------------------------------------------
/deploy/helm/grafana-operator/.helmignore:
--------------------------------------------------------------------------------
1 | # Patterns to ignore when building packages.
2 | # This supports shell glob matching, relative path matching, and
3 | # negation (prefixed with !). Only one pattern per line.
4 | .DS_Store
5 | # Common VCS dirs
6 | .git/
7 | .gitignore
8 | .bzr/
9 | .bzrignore
10 | .hg/
11 | .hgignore
12 | .svn/
13 | # Common backup files
14 | *.swp
15 | *.bak
16 | *.tmp
17 | *.orig
18 | *~
19 | # Various IDEs
20 | .project
21 | .idea/
22 | *.tmproj
23 | .vscode/
24 |
--------------------------------------------------------------------------------
/deploy/helm/grafana-operator/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: grafana-operator
3 | description: Helm chart for the Grafana Operator
4 | # A chart can be either an 'application' or a 'library' chart.
5 | #
6 | # Application charts are a collection of templates that can be packaged into versioned archives
7 | # to be deployed.
8 | #
9 | # Library charts provide useful utilities or functions for the chart developer. They're included as
10 | # a dependency of application charts to inject those utilities and functions into the rendering
11 | # pipeline. Library charts do not define any templates and therefore cannot be deployed.
12 | type: application
13 | # We keep the version and appVersion in sync as most updates also include
14 | # changes to the CRDs which are bundled with the helm resources
15 | version: v5.18.0
16 | appVersion: "v5.18.0"
17 |
--------------------------------------------------------------------------------
/deploy/helm/grafana-operator/files/rbac-openshift.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: manager-role
6 | rules:
7 | - apiGroups:
8 | - route.openshift.io
9 | resources:
10 | - routes
11 | - routes/custom-host
12 | verbs:
13 | - create
14 | - delete
15 | - get
16 | - list
17 | - update
18 | - watch
19 |
--------------------------------------------------------------------------------
/deploy/helm/grafana-operator/templates/dashboard.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.dashboard.enabled -}}
2 | apiVersion: v1
3 | kind: ConfigMap
4 | metadata:
5 | name: {{ include "grafana-operator.fullname" . }}-dashboard
6 | namespace: {{ include "grafana-operator.namespace" . }}
7 | labels:
8 | {{- include "grafana-operator.labels" . | nindent 4 }}
9 | app.kubernetes.io/component: operator
10 | {{- with .Values.dashboard.labels }}
11 | {{- toYaml . | nindent 4 }}
12 | {{- end }}
13 | {{- with .Values.dashboard.annotations }}
14 | annotations:
15 | {{- toYaml . | nindent 4 }}
16 | {{- end }}
17 | data:
18 | grafana-operator.json: |-
19 | {{- .Files.Get "files/dashboard.json" | nindent 4 }}
20 | {{- end -}}
21 |
--------------------------------------------------------------------------------
/deploy/helm/grafana-operator/templates/extraobjects.yaml:
--------------------------------------------------------------------------------
1 | {{ range .Values.extraObjects }}
2 | ---
3 | {{ tpl (toYaml .) $ }}
4 | {{ end }}
5 |
--------------------------------------------------------------------------------
/deploy/helm/grafana-operator/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: {{ include "grafana-operator.fullname" . }}-metrics-service
5 | namespace: {{ include "grafana-operator.namespace" . }}
6 | labels:
7 | {{- include "grafana-operator.labels" . | nindent 4 }}
8 | app.kubernetes.io/component: operator
9 | spec:
10 | type: {{ .Values.metricsService.type }}
11 | ports:
12 | - port: {{ .Values.metricsService.metricsPort }}
13 | targetPort: metrics
14 | protocol: TCP
15 | name: metrics
16 | - port: {{ .Values.metricsService.pprofPort }}
17 | targetPort: pprof
18 | protocol: TCP
19 | name: pprof
20 | selector:
21 | {{- include "grafana-operator.selectorLabels" . | nindent 4 }}
22 |
--------------------------------------------------------------------------------
/deploy/helm/grafana-operator/templates/serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.serviceAccount.create -}}
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: {{ include "grafana-operator.serviceAccountName" . }}
6 | namespace: {{ include "grafana-operator.namespace" . }}
7 | labels:
8 | {{- include "grafana-operator.labels" . | nindent 4 }}
9 | app.kubernetes.io/component: operator
10 | {{- with .Values.serviceAccount.annotations }}
11 | annotations:
12 | {{- toYaml . | nindent 4 }}
13 | {{- end }}
14 | automountServiceAccountToken: true
15 | {{- end }}
16 |
--------------------------------------------------------------------------------
/deploy/kustomize/README.md:
--------------------------------------------------------------------------------
1 | # Deploy with kustomize
2 |
3 | Two overlays are provided, for namespace scoped and cluster scoped installation.
4 | To install the Grafana operator, select one of the overlays and edit its `kustomization.yaml` file.
5 | Make sure `namespace` is set to the namespace where you want to install the operator.
6 | Then run:
7 |
8 | ```shell
9 | kustomize build deploy/kustomize/overlays/cluster_scoped --load-restrictor LoadRestrictionsNone | kubectl apply -f -
10 | ```
11 |
12 | for a cluster scoped installation, or:
13 |
14 | ```shell
15 | kustomize build deploy/kustomize/overlays/namespace_scoped --load-restrictor LoadRestrictionsNone | kubectl apply -f -
16 | ```
17 |
18 | for a namespace scoped installation.
19 |
20 | When you want to patch the grafana operator instead of using `kubectl apply` you need to use `kubectl replace`.
21 | Else you will get the following error `invalid: metadata.annotations: Too long: must have at most 262144 bytes`.
22 |
23 | For example
24 |
25 | ```shell
26 | kustomize build deploy/kustomize/overlays/namespace_scoped --load-restrictor LoadRestrictionsNone | kubectl replace -f -
27 | ```
28 |
--------------------------------------------------------------------------------
/deploy/kustomize/base/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - crds.yaml
3 | - namespace.yaml
4 | - serviceaccount.yaml
5 | - service.yaml
6 | - deployment.yaml
7 | - rolebinding.yaml
8 | - role.yaml
9 | patches:
10 | - target:
11 | kind: ClusterRole
12 | patch: |
13 | - op: replace
14 | path: /metadata/name
15 | value: grafana-operator-permissions
16 | images:
17 | - name: ghcr.io/grafana/grafana-operator
18 | newTag: v5.18.0
19 |
--------------------------------------------------------------------------------
/deploy/kustomize/base/namespace.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Namespace
4 | metadata:
5 | name: default
6 |
--------------------------------------------------------------------------------
/deploy/kustomize/base/rolebinding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: grafana-operator-permissions
5 | subjects:
6 | - kind: ServiceAccount
7 | name: grafana-operator-controller-manager
8 | namespace: default
9 | roleRef:
10 | kind: ClusterRole
11 | name: grafana-operator-permissions
12 | apiGroup: rbac.authorization.k8s.io
13 |
--------------------------------------------------------------------------------
/deploy/kustomize/base/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: grafana-operator-metrics-service
5 | labels:
6 | app.kubernetes.io/name: grafana-operator
7 | spec:
8 | type: ClusterIP
9 | ports:
10 | - port: 9090
11 | targetPort: metrics
12 | protocol: TCP
13 | name: metrics
14 | - port: 8888
15 | targetPort: pprof
16 | protocol: TCP
17 | name: pprof
18 | selector:
19 | app.kubernetes.io/name: grafana-operator
20 |
--------------------------------------------------------------------------------
/deploy/kustomize/base/serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: grafana-operator-controller-manager
6 | namespace: default
7 | automountServiceAccountToken: true
8 |
--------------------------------------------------------------------------------
/deploy/kustomize/overlays/chainsaw/deployment-patch.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: controller-manager
5 | spec:
6 | template:
7 | spec:
8 | containers:
9 | - name: manager
10 | imagePullPolicy: Never
11 | resources:
12 | limits:
13 | cpu: 400m
14 | memory: 1024Mi
15 | requests:
16 | cpu: 100m
17 | memory: 200Mi
18 | volumeMounts:
19 | - name: dashboards-dir
20 | mountPath: /tmp/dashboards
21 | volumes:
22 | - name: dashboards-dir
23 | emptyDir: {}
24 |
--------------------------------------------------------------------------------
/deploy/kustomize/overlays/chainsaw/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - ../../base
3 |
4 | patches:
5 | - path: deployment-patch.yaml
6 | target:
7 | group: apps
8 | kind: Deployment
9 | version: v1
10 | apiVersion: kustomize.config.k8s.io/v1beta1
11 | kind: Kustomization
12 | images:
13 | - name: ghcr.io/grafana/grafana-operator
14 | newName: ko.local/grafana/grafana-operator
15 | newTag: latest
16 |
--------------------------------------------------------------------------------
/deploy/kustomize/overlays/cluster_scoped/kustomization.yaml:
--------------------------------------------------------------------------------
1 | namespace: grafana
2 |
3 | resources:
4 | - ../../base
5 |
--------------------------------------------------------------------------------
/deploy/kustomize/overlays/namespace_scoped/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: grafana-operator-controller-manager
5 | spec:
6 | template:
7 | spec:
8 | containers:
9 | - name: manager
10 | env:
11 | - name: WATCH_NAMESPACE
12 | valueFrom:
13 | fieldRef:
14 | fieldPath: metadata.namespace
15 |
--------------------------------------------------------------------------------
/deploy/kustomize/overlays/namespace_scoped/kustomization.yaml:
--------------------------------------------------------------------------------
1 | namespace: grafana
2 |
3 | resources:
4 | - ../../base
5 |
6 | patches:
7 | - path: deployment.yaml
8 | target:
9 | kind: Deployment
10 | version: v1
11 | group: apps
12 | name: grafana-operator-controller-manager
13 | - target:
14 | kind: ClusterRole
15 | patch: |
16 | - op: replace
17 | path: /kind
18 | value: Role
19 | - target:
20 | kind: ClusterRoleBinding
21 | patch: |
22 | - op: replace
23 | path: /kind
24 | value: RoleBinding
25 | - op: replace
26 | path: /roleRef/kind
27 | value: Role
28 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Grafana-operator
2 |
3 | The docs folder contain all the grafana-operator documentation.
4 | It's a soft link to our hugo docs and our homepage [https://grafana-operator.github.io/grafana-operator/](https://grafana-operator.github.io/grafana-operator/) which automatically gets update when any changes is done in these files.
5 |
6 | Feel free to contribute to the docs if you feel that something is missing, you can find more information on how to contribute under [CONTRIBUTING.md](../CONTRIBUTING.md).
7 |
--------------------------------------------------------------------------------
/docs/about/_index.html:
--------------------------------------------------------------------------------
1 | ---
2 | title: About Grafana-Operator
3 | linkTitle: About
4 | menu:
5 | main:
6 | weight: 10
7 | ---
8 |
9 | {{< blocks/section >}}
10 |
11 |
12 |
About Grafana-Operator
13 |
14 |
Creation
15 |
16 | The Grafana-Operator was initially created by a number of RedHat employees as an internal project to manage their grafana instance.
17 | But the project was never supposed to be a part of a RedHat product and was made open-source within an external github organization.
18 | And was mostly maintained by few people from the original team that created it.
19 |
20 |
21 |
22 |
Current
23 |
24 | Currently the operator is maintained by Grafana Labs and the original creators/maintainers.
25 | While Grafana Labs maintains the operator, we do not offer any commercial support for it.
26 |
27 | Support & Issue triage is done on a best-effort basis.
28 |
29 |
30 |
31 | {{< /blocks/section >}}
32 |
--------------------------------------------------------------------------------
/docs/blog/_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Docsy Blog"
3 | linkTitle: "Blog"
4 | menu:
5 | main:
6 | weight: 30
7 | ---
8 |
9 |
10 | This is the **blog** section. It has two categories: News and Releases.
11 |
12 | Files in these directories will be listed in reverse chronological order.
13 |
--------------------------------------------------------------------------------
/docs/blog/flux-gitops/grafana-operator.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: source.toolkit.fluxcd.io/v1beta2
2 | kind: OCIRepository
3 | metadata:
4 | name: grafana-operator
5 | namespace: flux-system
6 | spec:
7 | interval: 10m
8 | url: oci://ghcr.io/grafana/kustomize/grafana-operator
9 | ref:
10 | tag: v5.0.0-rc3
11 | ---
12 | apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
13 | kind: Kustomization
14 | metadata:
15 | name: grafana-operator
16 | namespace: flux-system
17 | spec:
18 | interval: 10m
19 | targetNamespace: grafana
20 | images:
21 | - name: ghcr.io/grafana/grafana-operator
22 | newTag: v5.0.0-rc3
23 | prune: true
24 | sourceRef:
25 | kind: OCIRepository
26 | name: grafana-operator
27 | path: ./overlays/namespace_scoped
28 |
--------------------------------------------------------------------------------
/docs/blog/flux-gitops/grafana.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
2 | kind: Kustomization
3 | metadata:
4 | name: grafana
5 | namespace: flux-system
6 | spec:
7 | force: false
8 | dependsOn:
9 | - name: grafana-operator # Depends on the grafana-operator being synced
10 | healthChecks: # Check that grafana-deployment comes up
11 | - apiVersion: apps/v1
12 | kind: Deployment
13 | name: grafana-deployment
14 | namespace: grafana
15 | interval: 10m0s
16 | targetNamespace: grafana
17 | path: ./clusters/my-cluster/grafana
18 | prune: true
19 | sourceRef:
20 | kind: GitRepository
21 | name: flux-system
22 |
--------------------------------------------------------------------------------
/docs/blog/flux-gitops/grafana/dashboard.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDashboard
3 | metadata:
4 | name: grafanadashboard-sample
5 | namespace: grafana
6 | spec:
7 | resyncPeriod: 30s
8 | instanceSelector:
9 | matchLabels:
10 | dashboards: "grafana"
11 | json: >
12 | {
13 | "id": null,
14 | "title": "Simple Dashboard",
15 | "tags": [],
16 | "style": "dark",
17 | "timezone": "browser",
18 | "editable": true,
19 | "hideControls": false,
20 | "graphTooltip": 1,
21 | "panels": [],
22 | "time": {
23 | "from": "now-6h",
24 | "to": "now"
25 | },
26 | "timepicker": {
27 | "time_options": [],
28 | "refresh_intervals": []
29 | },
30 | "templating": {
31 | "list": []
32 | },
33 | "annotations": {
34 | "list": []
35 | },
36 | "refresh": "5s",
37 | "schemaVersion": 17,
38 | "version": 0,
39 | "links": []
40 | }
41 |
--------------------------------------------------------------------------------
/docs/blog/flux-gitops/grafana/grafana.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: Grafana
3 | metadata:
4 | name: grafana
5 | namespace: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | config:
10 | log:
11 | mode: "console"
12 | auth:
13 | disable_login_form: "false"
14 | deployment:
15 | spec:
16 | template:
17 | spec:
18 | containers:
19 | - name: grafana
20 | env:
21 | - name: GF_SECURITY_ADMIN_USER
22 | valueFrom:
23 | secretKeyRef:
24 | key: GF_SECURITY_ADMIN_USER
25 | name: credentials
26 | - name: GF_SECURITY_ADMIN_PASSWORD
27 | valueFrom:
28 | secretKeyRef:
29 | key: GF_SECURITY_ADMIN_PASSWORD
30 | name: credentials
31 | ingress:
32 | spec:
33 | ingressClassName: nginx
34 | rules:
35 | - host: grafana.127.0.0.1.nip.io
36 | http:
37 | paths:
38 | - backend:
39 | service:
40 | name: grafana-service
41 | port:
42 | number: 3000
43 | path: /
44 | pathType: Prefix
45 |
--------------------------------------------------------------------------------
/docs/blog/flux-gitops/grafana/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 | resources:
4 | - grafana.yaml
5 | - dashboard.yaml
6 |
--------------------------------------------------------------------------------
/docs/docs/_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Introduction"
3 | linkTitle: "Documentation"
4 | weight: 20
5 | menu:
6 | main:
7 | weight: 20
8 | ---
9 |
10 | The Grafana operator allows you to:
11 | * ⚙️ Deploy & Manage Grafana Instances inside of Kubernetes with ease
12 | * 🌐 Manage externally hosted instances using Kubernetes resources (for example Grafana Cloud)
13 |
14 | To install the Grafana Operator in your Kubernetes cluster, Run the following command in your terminal:
15 |
16 | ```bash
17 | helm upgrade -i grafana-operator oci://ghcr.io/grafana/helm-charts/grafana-operator --version {{}}
18 | ```
19 |
20 | For a detailed installation guide, refer to [the installation documentation]({{}}).
21 |
22 | To get started, take a look at the [quick start guide]({{}}).
23 |
--------------------------------------------------------------------------------
/docs/docs/alerting/alert-rule-groups.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Alert Rule Groups
3 | ---
4 |
5 | Alert Rule Groups contain a list of alerts which should evaluate at the same interval.
6 | Every rule group must belong to a folder and contain at least one rule.
7 |
8 | The easiest way to get the YAML specification for an alert rule is to use the [modify export feature](https://grafana.com/docs/grafana/latest/alerting/set-up/provision-alerting-resources/export-alerting-resources/), introduced in Grafana 10.
9 |
10 | The following snippet shows an example alert rule group with a single alert that fires when the temperature is below zero degrees.
11 |
12 | {{< readfile file="../examples/alertrulegroups/resources.yaml" code="true" lang="yaml" >}}
13 |
--------------------------------------------------------------------------------
/docs/docs/alerting/contact-points.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Contact Points
3 | ---
4 |
5 | Contact points contain the configuration for sending alert notifications. You can assign a contact point either in the alert rule or notification policy options.
6 | For a complete explanation on notification policies, refer to the [upstream Grafana documentation](https://grafana.com/docs/grafana/latest/alerting/fundamentals/notifications/contact-points/).
7 |
8 | {{% alert title="Note" color="secondary" %}}
9 | The Grafana operator currently only supports a single receiver per contact point definition.
10 | As a workaround you can create multiple contact points with the same `spec.name` value.
11 | Follow issue [#1529](https://github.com/grafana/grafana-operator/issues/1529) for further updates on this topic.
12 | {{% /alert %}}
13 |
14 | The following snippet shows an example contact point which notifies a specific email address.
15 | It also highlights how secrets and config maps can utilized to externalize some of the configuration.
16 | This is especially useful for contact points which contain sensitive information.
17 |
18 | {{< readfile file="../examples/contactpoint_override/resources.yaml" code="true" lang="yaml" >}}
19 |
--------------------------------------------------------------------------------
/docs/docs/alerting/dynamic-notification-policy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grafana/grafana-operator/953272667a20556c0a6d4fcd63f403170a160040/docs/docs/alerting/dynamic-notification-policy.png
--------------------------------------------------------------------------------
/docs/docs/alerting/notification-policy-tree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grafana/grafana-operator/953272667a20556c0a6d4fcd63f403170a160040/docs/docs/alerting/notification-policy-tree.png
--------------------------------------------------------------------------------
/docs/docs/alerting/notification-routing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grafana/grafana-operator/953272667a20556c0a6d4fcd63f403170a160040/docs/docs/alerting/notification-routing.png
--------------------------------------------------------------------------------
/docs/docs/alerting/overview-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grafana/grafana-operator/953272667a20556c0a6d4fcd63f403170a160040/docs/docs/alerting/overview-page.png
--------------------------------------------------------------------------------
/docs/docs/installation/_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Installation"
3 | linkTitle: "Installation"
4 | weight: 10
5 | ---
6 |
7 | The grafana-operator supports multiple different installation methods.
8 |
9 | - Helm
10 | - Kustomize
11 | - Openshift OLM
12 |
--------------------------------------------------------------------------------
/docs/docs/proposals/_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Proposals"
3 | linkTitle: "Proposals"
4 | weight: 300
5 | ---
6 |
--------------------------------------------------------------------------------
/docs/docs/security.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Security
3 | weight: 50
4 | ---
5 |
6 | ## Verification of container images
7 |
8 | Grafana-operator container images are signed by cosign using identity-based ("keyless") signing and transparency. Executing the following command can be used to verify the signature of a container image:
9 |
10 | To verify the grafana-operator run
11 |
12 | Pre-requirement
13 |
14 | - cosign v2.0.0 or higher [installation instructions](https://docs.sigstore.dev/system_config/installation/).
15 |
16 | ```shell
17 | cosign verify ghcr.io/grafana/grafana-operator@ \
18 | --certificate-identity-regexp 'https://github\.com/grafana/grafana-operator/\.github/workflows/.+' \
19 | --certificate-oidc-issuer https://token.actions.githubusercontent.com | jq
20 | ```
21 |
22 | For example
23 |
24 | ```shell
25 | cosign verify ghcr.io/grafana/grafana-operator@v5.6.1 \
26 | --certificate-identity-regexp 'https://github\.com/grafana/grafana-operator/\.github/workflows/.+' \
27 | --certificate-oidc-issuer https://token.actions.githubusercontent.com | jq
28 | ```
29 |
30 | ## SBOM
31 |
32 | As a part of our release cycle we also generate SBOMs.
33 | You can find them as artifacts in our supported repositories.
34 |
35 | To download the sbom you can run
36 |
37 | ```shell
38 | cosign download sbom --platform linux/amd64 ghcr.io/grafana/grafana-operator:
39 | ```
40 |
41 | example:
42 |
43 | ```shell
44 | cosign download sbom --platform linux/amd64 ghcr.io/grafana/grafana-operator:v5.6.1
45 | ```
46 |
--------------------------------------------------------------------------------
/embeds/README.md:
--------------------------------------------------------------------------------
1 | # Embeds
2 |
3 | This package contains static information compiled into the binary at build time.
4 |
5 | # Grafonnet
6 |
7 | The Grafonnet Jsonnet is used to allow users to define their dashboards using the jsonnet framework.
8 |
9 | # Version
10 |
11 | The `Version` variable is set during production builds by `ko`. Configuration for this can be found in `.ko.yaml`
12 |
--------------------------------------------------------------------------------
/embeds/grafana_embeds.go:
--------------------------------------------------------------------------------
1 | package embeds
2 |
3 | import "embed"
4 |
5 | //go:embed grafonnet-lib
6 | var GrafonnetEmbed embed.FS
7 |
8 | //go:embed testing/dashboard.jsonnet
9 | var TestDashboardEmbed []byte
10 |
11 | //go:embed testing/dashboard.json
12 | var TestDashboardEmbedExpectedJSON []byte
13 |
14 | //go:embed testing/dashboard_with_envs.jsonnet
15 | var TestDashboardEmbedWithEnv []byte
16 |
17 | //go:embed testing/dashboard_with_provided_envs.json
18 | var TestDashboardEmbedWithEnvExpectedJSON []byte
19 |
20 | //go:embed testing/jsonnetProjectWithRuntimeRaw.tar.gz
21 | var TestJsonnetProjectBuildFolderGzip []byte
22 |
23 | // this variable is replaced during production builds
24 | var Version = "dev"
25 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet-7.0/grafana.libsonnet:
--------------------------------------------------------------------------------
1 | // This file was generated by https://github.com/grafana/dashboard-spec
2 |
3 | {
4 | dashboard:: import 'dashboard.libsonnet',
5 | panel:: {
6 | gauge:: import 'panel/gauge.libsonnet',
7 | graph:: import 'panel/graph.libsonnet',
8 | row:: import 'panel/row.libsonnet',
9 | stat:: import 'panel/stat.libsonnet',
10 | table:: import 'panel/table.libsonnet',
11 | text:: import 'panel/text.libsonnet',
12 | },
13 | target:: {
14 | prometheus:: import 'target/prometheus.libsonnet',
15 | },
16 | template:: {
17 | custom:: import 'template/custom.libsonnet',
18 | datasource:: import 'template/datasource.libsonnet',
19 | query:: import 'template/query.libsonnet',
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet-7.0/panel/row.libsonnet:
--------------------------------------------------------------------------------
1 | // This file was generated by https://github.com/grafana/dashboard-spec
2 |
3 | {
4 | new(
5 | collapse=true,
6 | collapsed=true,
7 | datasource=null,
8 | repeat=null,
9 | repeatIteration=null,
10 | showTitle=true,
11 | title=null,
12 | titleSize='h6',
13 | ):: {
14 | [if collapse != null then 'collapse']: collapse,
15 | [if collapsed != null then 'collapsed']: collapsed,
16 | [if datasource != null then 'datasource']: datasource,
17 | [if repeat != null then 'repeat']: repeat,
18 | [if repeatIteration != null then 'repeatIteration']: repeatIteration,
19 | [if showTitle != null then 'showTitle']: showTitle,
20 | [if title != null then 'title']: title,
21 | [if titleSize != null then 'titleSize']: titleSize,
22 | type: 'row',
23 |
24 | setGridPos(
25 | h=8,
26 | w=12,
27 | x=null,
28 | y=null,
29 | ):: self {}
30 | + { gridPos+: { [if h != null then 'h']: h } }
31 | + { gridPos+: { [if w != null then 'w']: w } }
32 | + { gridPos+: { [if x != null then 'x']: x } }
33 | + { gridPos+: { [if y != null then 'y']: y } }
34 | ,
35 |
36 |
37 | addPanel(
38 | panel
39 | ):: self {}
40 | + { panels+: [
41 | panel,
42 | ] },
43 |
44 | },
45 | }
46 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet-7.0/target/prometheus.libsonnet:
--------------------------------------------------------------------------------
1 | // This file was generated by https://github.com/grafana/dashboard-spec
2 |
3 | {
4 | new(
5 | datasource='default',
6 | expr=null,
7 | format='time_series',
8 | instant=null,
9 | interval=null,
10 | intervalFactor=null,
11 | legendFormat=null,
12 | ):: {
13 | [if datasource != null then 'datasource']: datasource,
14 | [if expr != null then 'expr']: expr,
15 | [if format != null then 'format']: format,
16 | [if instant != null then 'instant']: instant,
17 | [if interval != null then 'interval']: interval,
18 | [if intervalFactor != null then 'intervalFactor']: intervalFactor,
19 | [if legendFormat != null then 'legendFormat']: legendFormat,
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet-7.0/template/custom.libsonnet:
--------------------------------------------------------------------------------
1 | // This file was generated by https://github.com/grafana/dashboard-spec
2 |
3 | {
4 | new(
5 | allValue=null,
6 | hide=0,
7 | includeAll=false,
8 | label=null,
9 | multi=false,
10 | name=null,
11 | query=null,
12 | queryValue='',
13 | skipUrlSync=false,
14 | ):: {
15 | [if allValue != null then 'allValue']: allValue,
16 | [if hide != null then 'hide']: hide,
17 | [if includeAll != null then 'includeAll']: includeAll,
18 | [if label != null then 'label']: label,
19 | [if multi != null then 'multi']: multi,
20 | [if name != null then 'name']: name,
21 | [if query != null then 'query']: query,
22 | [if queryValue != null then 'queryValue']: queryValue,
23 | [if skipUrlSync != null then 'skipUrlSync']: skipUrlSync,
24 | type: 'custom',
25 |
26 | setCurrent(
27 | selected=false,
28 | text=null,
29 | value=null,
30 | ):: self {}
31 | + { current+: { [if selected != null then 'selected']: selected } }
32 | + { current+: { [if text != null then 'text']: text } }
33 | + { current+: { [if value != null then 'value']: value } },
34 |
35 | },
36 | }
37 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet-7.0/template/datasource.libsonnet:
--------------------------------------------------------------------------------
1 | // This file was generated by https://github.com/grafana/dashboard-spec
2 |
3 | {
4 | new(
5 | hide=0,
6 | includeAll=false,
7 | label=null,
8 | multi=false,
9 | name=null,
10 | query=null,
11 | refresh=1,
12 | regex=null,
13 | skipUrlSync=false,
14 | ):: {
15 | [if hide != null then 'hide']: hide,
16 | [if includeAll != null then 'includeAll']: includeAll,
17 | [if label != null then 'label']: label,
18 | [if multi != null then 'multi']: multi,
19 | [if name != null then 'name']: name,
20 | [if query != null then 'query']: query,
21 | [if refresh != null then 'refresh']: refresh,
22 | [if regex != null then 'regex']: regex,
23 | [if skipUrlSync != null then 'skipUrlSync']: skipUrlSync,
24 | type: 'datasource',
25 |
26 | setCurrent(
27 | selected=false,
28 | text=null,
29 | value=null,
30 | ):: self {}
31 | + { current+: { [if selected != null then 'selected']: selected } }
32 | + { current+: { [if text != null then 'text']: text } }
33 | + { current+: { [if value != null then 'value']: value } },
34 |
35 | },
36 | }
37 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet/annotation.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | default::
3 | {
4 | builtIn: 1,
5 | datasource: '-- Grafana --',
6 | enable: true,
7 | hide: true,
8 | iconColor: 'rgba(0, 211, 255, 1)',
9 | name: 'Annotations & Alerts',
10 | type: 'dashboard',
11 | },
12 |
13 | /**
14 | * @name annotation.datasource
15 | */
16 |
17 | datasource(
18 | name,
19 | datasource,
20 | expr=null,
21 | enable=true,
22 | hide=false,
23 | iconColor='rgba(255, 96, 96, 1)',
24 | tags=[],
25 | type='tags',
26 | builtIn=null,
27 | )::
28 | {
29 | datasource: datasource,
30 | enable: enable,
31 | [if expr != null then 'expr']: expr,
32 | hide: hide,
33 | iconColor: iconColor,
34 | name: name,
35 | showIn: 0,
36 | tags: tags,
37 | type: type,
38 | [if builtIn != null then 'builtIn']: builtIn,
39 | },
40 | }
41 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet/bar_gauge_panel.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | * Create a [bar gauge panel](https://grafana.com/docs/grafana/latest/panels/visualizations/bar-gauge-panel/),
4 | *
5 | * @name barGaugePanel.new
6 | *
7 | * @param title Panel title.
8 | * @param description (optional) Panel description.
9 | * @param datasource (optional) Panel datasource.
10 | * @param unit (optional) The unit of the data.
11 | * @param thresholds (optional) An array of threashold values.
12 | *
13 | * @method addTarget(target) Adds a target object.
14 | * @method addTargets(targets) Adds an array of targets.
15 | */
16 | new(
17 | title,
18 | description=null,
19 | datasource=null,
20 | unit=null,
21 | thresholds=[],
22 | ):: {
23 | type: 'bargauge',
24 | title: title,
25 | [if description != null then 'description']: description,
26 | datasource: datasource,
27 | targets: [
28 | ],
29 | fieldConfig: {
30 | defaults: {
31 | unit: unit,
32 | thresholds: {
33 | mode: 'absolute',
34 | steps: thresholds,
35 | },
36 | },
37 | },
38 | _nextTarget:: 0,
39 | addTarget(target):: self {
40 | // automatically ref id in added targets.
41 | local nextTarget = super._nextTarget,
42 | _nextTarget: nextTarget + 1,
43 | targets+: [target { refId: std.char(std.codepoint('A') + nextTarget) }],
44 | },
45 | addTargets(targets):: std.foldl(function(p, t) p.addTarget(t), targets, self),
46 | },
47 | }
48 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet/cloudwatch.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | * Creates a [CloudWatch target](https://grafana.com/docs/grafana/latest/datasources/cloudwatch/)
4 | *
5 | * @name cloudwatch.target
6 | *
7 | * @param region
8 | * @param namespace
9 | * @param metric
10 | * @param datasource (optional)
11 | * @param statistic (default: `'Average'`)
12 | * @param alias (optional)
13 | * @param highResolution (default: `false`)
14 | * @param period (default: `'auto'`)
15 | * @param dimensions (optional)
16 | * @param id (optional)
17 | * @param expression (optional)
18 | * @param hide (optional)
19 |
20 | * @return Panel target
21 | */
22 |
23 | target(
24 | region,
25 | namespace,
26 | metric,
27 | datasource=null,
28 | statistic='Average',
29 | alias=null,
30 | highResolution=false,
31 | period='auto',
32 | dimensions={},
33 | id=null,
34 | expression=null,
35 | hide=null
36 | ):: {
37 | region: region,
38 | namespace: namespace,
39 | metricName: metric,
40 | [if datasource != null then 'datasource']: datasource,
41 | statistics: [statistic],
42 | [if alias != null then 'alias']: alias,
43 | highResolution: highResolution,
44 | period: period,
45 | dimensions: dimensions,
46 | [if id != null then 'id']: id,
47 | [if expression != null then 'expression']: expression,
48 | [if hide != null then 'hide']: hide,
49 |
50 | },
51 | }
52 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet/dashlist.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | * Creates a [dashlist panel](https://grafana.com/docs/grafana/latest/panels/visualizations/dashboard-list-panel/).
4 | * It requires the dashlist panel plugin in grafana, which is built-in.
5 | *
6 | * @name dashlist.new
7 | *
8 | * @param title The title of the dashlist panel.
9 | * @param description (optional) Description of the panel
10 | * @param query (optional) Query to search by
11 | * @param tags (optional) Array of tag(s) to search by
12 | * @param recent (default `true`) Displays recently viewed dashboards
13 | * @param search (default `false`) Description of the panel
14 | * @param starred (default `false`) Displays starred dashboards
15 | * @param headings (default `true`) Chosen list selection(starred, recently Viewed, search) is shown as a heading
16 | * @param limit (default `10`) Set maximum items in a list
17 | * @return A json that represents a dashlist panel
18 | */
19 | new(
20 | title,
21 | description=null,
22 | query=null,
23 | tags=[],
24 | recent=true,
25 | search=false,
26 | starred=false,
27 | headings=true,
28 | limit=10,
29 | ):: {
30 | type: 'dashlist',
31 | title: title,
32 | query: if query != null then query else '',
33 | tags: tags,
34 | recent: recent,
35 | search: search,
36 | starred: starred,
37 | headings: headings,
38 | limit: limit,
39 | [if description != null then 'description']: description,
40 | },
41 | }
42 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet/elasticsearch.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | * Creates an [Elasticsearch target](https://grafana.com/docs/grafana/latest/datasources/elasticsearch/)
4 | *
5 | * @name elasticsearch.target
6 | *
7 | * @param query
8 | * @param timeField
9 | * @param id (optional)
10 | * @param datasource (optional)
11 | * @param metrics (optional)
12 | * @param bucketAggs (optional)
13 | * @param alias (optional)
14 | */
15 | target(
16 | query,
17 | timeField,
18 | id=null,
19 | datasource=null,
20 | metrics=[{
21 | field: 'value',
22 | id: null,
23 | type: 'percentiles',
24 | settings: {
25 | percents: [
26 | '90',
27 | ],
28 | },
29 | }],
30 | bucketAggs=[{
31 | field: 'timestamp',
32 | id: null,
33 | type: 'date_histogram',
34 | settings: {
35 | interval: '1s',
36 | min_doc_count: 0,
37 | trimEdges: 0,
38 | },
39 | }],
40 | alias=null,
41 | ):: {
42 | [if datasource != null then 'datasource']: datasource,
43 | query: query,
44 | id: id,
45 | timeField: timeField,
46 | bucketAggs: bucketAggs,
47 | metrics: metrics,
48 | alias: alias,
49 | // TODO: generate bucket ids
50 | },
51 | }
52 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet/grafana.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | alertlist:: import 'alertlist.libsonnet',
3 | dashboard:: import 'dashboard.libsonnet',
4 | template:: import 'template.libsonnet',
5 | text:: import 'text.libsonnet',
6 | timepicker:: import 'timepicker.libsonnet',
7 | row:: import 'row.libsonnet',
8 | link:: import 'link.libsonnet',
9 | annotation:: import 'annotation.libsonnet',
10 | graphPanel:: import 'graph_panel.libsonnet',
11 | logPanel:: import 'log_panel.libsonnet',
12 | tablePanel:: import 'table_panel.libsonnet',
13 | singlestat:: import 'singlestat.libsonnet',
14 | pieChartPanel:: import 'pie_chart_panel.libsonnet',
15 | influxdb:: import 'influxdb.libsonnet',
16 | prometheus:: import 'prometheus.libsonnet',
17 | loki:: import 'loki.libsonnet',
18 | sql:: import 'sql.libsonnet',
19 | graphite:: import 'graphite.libsonnet',
20 | alertCondition:: import 'alert_condition.libsonnet',
21 | cloudmonitoring:: import 'cloudmonitoring.libsonnet',
22 | cloudwatch:: import 'cloudwatch.libsonnet',
23 | elasticsearch:: import 'elasticsearch.libsonnet',
24 | heatmapPanel:: import 'heatmap_panel.libsonnet',
25 | dashlist:: import 'dashlist.libsonnet',
26 | pluginlist:: import 'pluginlist.libsonnet',
27 | gauge:: error 'gauge is removed, migrate to gaugePanel',
28 | gaugePanel:: import 'gauge_panel.libsonnet',
29 | barGaugePanel:: import 'bar_gauge_panel.libsonnet',
30 | statPanel:: import 'stat_panel.libsonnet',
31 | transformation:: import 'transformation.libsonnet',
32 | }
33 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet/graphite.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | * Creates a [Graphite target](https://grafana.com/docs/grafana/latest/datasources/graphite/)
4 | *
5 | * @name graphite.target
6 | *
7 | * @param target Graphite Query. Nested queries are possible by adding the query reference (refId).
8 | * @param targetFull (optional) Expanding the @target. Used in nested queries.
9 | * @param hide (default `false`) Disable query on graph.
10 | * @param textEditor (default `false`) Enable raw query mode.
11 | * @param datasource (optional) Datasource.
12 |
13 | * @return Panel target
14 | */
15 | target(
16 | target,
17 | targetFull=null,
18 | hide=false,
19 | textEditor=false,
20 | datasource=null,
21 | ):: {
22 | target: target,
23 | hide: hide,
24 | textEditor: textEditor,
25 |
26 | [if targetFull != null then 'targetFull']: targetFull,
27 | [if datasource != null then 'datasource']: datasource,
28 | },
29 | }
30 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet/loki.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | * Creates a [Loki target](https://grafana.com/docs/grafana/latest/datasources/loki/)
4 | *
5 | * @name loki.target
6 | *
7 | * @param expr
8 | * @param hide (optional) Disable query on graph.
9 | * @param legendFormat (optional) Defines the legend. Defaults to ''.
10 | */
11 | target(
12 | expr,
13 | hide=null,
14 | legendFormat='',
15 | instant=null,
16 | ):: {
17 | [if hide != null then 'hide']: hide,
18 | expr: expr,
19 | legendFormat: legendFormat,
20 | [if instant != null then 'instant']: instant,
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet/pluginlist.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | * Returns a new pluginlist panel that can be added in a row.
4 | * It requires the pluginlist panel plugin in grafana, which is built-in.
5 | *
6 | * @name pluginlist.new
7 | *
8 | * @param title The title of the pluginlist panel.
9 | * @param description (optional) Description of the panel
10 | * @param limit (optional) Set maximum items in a list
11 | * @return A json that represents a pluginlist panel
12 | */
13 | new(
14 | title,
15 | description=null,
16 | limit=null,
17 | ):: {
18 | type: 'pluginlist',
19 | title: title,
20 | [if limit != null then 'limit']: limit,
21 | [if description != null then 'description']: description,
22 | },
23 | }
24 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet/sql.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | * Creates an SQL target.
4 | *
5 | * @name sql.target
6 | *
7 | * @param rawSql The SQL query
8 | * @param datasource (optional)
9 | * @param format (default `'time_series'`)
10 | * @param alias (optional)
11 | */
12 | target(
13 | rawSql,
14 | datasource=null,
15 | format='time_series',
16 | alias=null,
17 | ):: {
18 | [if datasource != null then 'datasource']: datasource,
19 | format: format,
20 | [if alias != null then 'alias']: alias,
21 | rawSql: rawSql,
22 | },
23 | }
24 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet/timepicker.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | * Creates a Timepicker
4 | *
5 | * @name timepicker.new
6 | *
7 | * @param refresh_intervals (default: `['5s','10s','30s','1m','5m','15m','30m','1h','2h','1d']`) Array of time durations
8 | * @param time_options (default: `['5m','15m','1h','6h','12h','24h','2d','7d','30d']`) Array of time durations
9 | */
10 | new(
11 | refresh_intervals=[
12 | '5s',
13 | '10s',
14 | '30s',
15 | '1m',
16 | '5m',
17 | '15m',
18 | '30m',
19 | '1h',
20 | '2h',
21 | '1d',
22 | ],
23 | time_options=[
24 | '5m',
25 | '15m',
26 | '1h',
27 | '6h',
28 | '12h',
29 | '24h',
30 | '2d',
31 | '7d',
32 | '30d',
33 | ],
34 | nowDelay=null,
35 | ):: {
36 | refresh_intervals: refresh_intervals,
37 | time_options: time_options,
38 | [if nowDelay != null then 'nowDelay']: nowDelay,
39 | },
40 | }
41 |
--------------------------------------------------------------------------------
/embeds/grafonnet-lib/grafonnet/transformation.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | * @name transformation.new
4 | */
5 | new(
6 | id='',
7 | options={}
8 | ):: {
9 | id: id,
10 | options: options,
11 | },
12 | }
13 |
--------------------------------------------------------------------------------
/embeds/testing/jsonnetProjectWithRuntimeRaw.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grafana/grafana-operator/953272667a20556c0a6d4fcd63f403170a160040/embeds/testing/jsonnetProjectWithRuntimeRaw.tar.gz
--------------------------------------------------------------------------------
/embeds/testing/jsonnetProjectWithRuntimeRaw/dashboard_with_envs.jsonnet:
--------------------------------------------------------------------------------
1 | local env = std.extVar('TEST_ENV');
2 | {
3 | "env" : env
4 | }
5 |
--------------------------------------------------------------------------------
/examples/_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Examples"
3 | linkTitle: "Examples"
4 | weight: 20
5 | ---
6 |
--------------------------------------------------------------------------------
/examples/alertrulegroups/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Alert rule groups"
3 | linkTitle: "Alert rule groups"
4 | ---
5 |
6 | Shows how to create an alert rule group, contained in a folder.
7 |
8 | The easiest way to build alerts is using the Grafana UI.
9 | After creating the rule, use the [Modify Export](https://grafana.com/docs/grafana/latest/alerting/set-up/provision-alerting-resources/export-alerting-resources/#modify-and-export-alert-rules-without-saving-changes) feature to get the correct YAML from your resource.
10 |
11 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
12 |
--------------------------------------------------------------------------------
/examples/basic/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Basic example"
3 | linkTitle: "Basic example"
4 | ---
5 |
6 | A basic deployment of Grafana with a dashboard.
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/basic/resources.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: Grafana
3 | metadata:
4 | name: grafana
5 | labels:
6 | dashboards: "grafana"
7 | spec:
8 | config:
9 | log:
10 | mode: "console"
11 | auth:
12 | disable_login_form: "false"
13 | security:
14 | admin_user: root
15 | admin_password: secret
16 | ---
17 | apiVersion: grafana.integreatly.org/v1beta1
18 | kind: GrafanaDashboard
19 | metadata:
20 | name: grafanadashboard-sample
21 | spec:
22 | resyncPeriod: 30s
23 | instanceSelector:
24 | matchLabels:
25 | dashboards: "grafana"
26 | json: >
27 | {
28 | "id": null,
29 | "title": "Simple Dashboard",
30 | "tags": [],
31 | "style": "dark",
32 | "timezone": "browser",
33 | "editable": true,
34 | "hideControls": false,
35 | "graphTooltip": 1,
36 | "panels": [],
37 | "time": {
38 | "from": "now-6h",
39 | "to": "now"
40 | },
41 | "timepicker": {
42 | "time_options": [],
43 | "refresh_intervals": []
44 | },
45 | "templating": {
46 | "list": []
47 | },
48 | "annotations": {
49 | "list": []
50 | },
51 | "refresh": "5s",
52 | "schemaVersion": 17,
53 | "version": 0,
54 | "links": []
55 | }
56 |
--------------------------------------------------------------------------------
/examples/configmaps_sidecar/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Dashboard from configmap via sidecar example"
3 | linkTitle: "Dashboard from configmap via sidecar example"
4 | ---
5 |
6 | Using the [kiwigrid/k8s-sidecar](https://github.com/kiwigrid/k8s-sidecar) to watch for configmaps and import their
7 | contents as dashboards.
8 |
9 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
10 |
--------------------------------------------------------------------------------
/examples/contactpoint_override/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Contact point overrides"
3 | linkTitle: "Contact point overrides"
4 | ---
5 |
6 | Contact point overrides allow you to set configuration values using secrets or configmaps instead of directly in the resource
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/contactpoint_override/resources.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: contact-mails
5 | stringData:
6 | alert-mails: "foo@example.com"
7 | ---
8 | apiVersion: grafana.integreatly.org/v1beta1
9 | kind: GrafanaContactPoint
10 | metadata:
11 | name: grafanacontactpoint-sample
12 | spec:
13 | name: grafanacontactpoint-sample
14 | type: "email"
15 | instanceSelector:
16 | matchLabels:
17 | instance: my-grafana-stack
18 | settings:
19 | subject: 'Grafana Alert'
20 | valuesFrom:
21 | - targetPath: addresses
22 | valueFrom:
23 | secretKeyRef:
24 | name: contact-mails
25 | key: alert-mails
26 |
--------------------------------------------------------------------------------
/examples/credential_config/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Credential config"
3 | linkTitle: "Credential config"
4 | ---
5 |
6 | This example shows how to set the admin account credentials via configuration.
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/credential_secret/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Credential Secret"
3 | linkTitle: "Credential Secret"
4 | ---
5 |
6 | This example shows how to provide you own Grafana admin credentials from an
7 | existing secret.
8 |
9 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
10 |
--------------------------------------------------------------------------------
/examples/credential_secret/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | kind: Secret
3 | apiVersion: v1
4 | metadata:
5 | name: credentials
6 | namespace: grafana
7 | stringData:
8 | GF_SECURITY_ADMIN_PASSWORD: secret
9 | GF_SECURITY_ADMIN_USER: root
10 | type: Opaque
11 | ---
12 | apiVersion: grafana.integreatly.org/v1beta1
13 | kind: Grafana
14 | metadata:
15 | name: grafana
16 | labels:
17 | dashboards: "grafana"
18 | spec:
19 | config:
20 | log:
21 | mode: "console"
22 | auth:
23 | disable_login_form: "false"
24 | disableDefaultAdminSecret: true
25 | deployment:
26 | spec:
27 | template:
28 | spec:
29 | containers:
30 | - name: grafana
31 | env:
32 | - name: GF_SECURITY_ADMIN_USER
33 | valueFrom:
34 | secretKeyRef:
35 | key: GF_SECURITY_ADMIN_USER
36 | name: credentials
37 | - name: GF_SECURITY_ADMIN_PASSWORD
38 | valueFrom:
39 | secretKeyRef:
40 | key: GF_SECURITY_ADMIN_PASSWORD
41 | name: credentials
42 |
--------------------------------------------------------------------------------
/examples/crossnamespace/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Cross namespace"
3 | linkTitle: "Cross namespace"
4 | ---
5 |
6 |
7 | If you want your dashboard or datasource to be able to be used by a grafana instance in another namespace you need to set `spec.allowCrossNamespaceImport: true`.
8 |
9 | In the resources file you will find examples how to do this.
10 |
11 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
12 |
--------------------------------------------------------------------------------
/examples/crossnamespace/resources.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: grafana-a
5 | ---
6 | apiVersion: v1
7 | kind: Namespace
8 | metadata:
9 | name: grafana-b
10 | ---
11 | apiVersion: grafana.integreatly.org/v1beta1
12 | kind: Grafana
13 | metadata:
14 | name: grafana
15 | namespace: grafana-a
16 | labels:
17 | dashboards: "grafana"
18 | spec:
19 | config:
20 | log:
21 | mode: "console"
22 | auth:
23 | disable_login_form: "false"
24 | security:
25 | admin_user: root
26 | admin_password: secret
27 | deployment:
28 | spec:
29 | template:
30 | spec:
31 | containers:
32 | - name: grafana
33 | securityContext:
34 | allowPrivilegeEscalation: true
35 | readOnlyRootFilesystem: false
36 | readinessProbe:
37 | failureThreshold: 3
38 | ---
39 | apiVersion: grafana.integreatly.org/v1beta1
40 | kind: GrafanaDatasource
41 | metadata:
42 | name: example-grafanadatasource
43 | namespace: grafana-b
44 | spec:
45 | allowCrossNamespaceImport: true
46 | datasource:
47 | access: proxy
48 | database: prometheus
49 | jsonData:
50 | timeInterval: 5s
51 | tlsSkipVerify: true
52 | name: Prometheus
53 | url: http://prometheus-service:9090
54 | instanceSelector:
55 | matchLabels:
56 | dashboards: grafana
57 |
--------------------------------------------------------------------------------
/examples/dashboard_from_configmap/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Dashboard from ConfigMap"
3 | linkTitle: "Dashboard from ConfigMap"
4 | ---
5 |
6 | Shows how to obtain the dashboard definition (json) from a key in a ConfigMap in the same namespace as the dashboard CR.
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/dashboard_from_configmap/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | config:
10 | log:
11 | mode: "console"
12 | auth:
13 | disable_login_form: "false"
14 | security:
15 | admin_user: root
16 | admin_password: secret
17 | ---
18 | apiVersion: v1
19 | kind: ConfigMap
20 | metadata:
21 | name: dashboard-definition
22 | data:
23 | json: >
24 | {
25 | "id": null,
26 | "title": "Simple Dashboard from ConfigMap",
27 | "tags": [],
28 | "style": "dark",
29 | "timezone": "browser",
30 | "editable": true,
31 | "hideControls": false,
32 | "graphTooltip": 1,
33 | "panels": [],
34 | "time": {
35 | "from": "now-6h",
36 | "to": "now"
37 | },
38 | "timepicker": {
39 | "time_options": [],
40 | "refresh_intervals": []
41 | },
42 | "templating": {
43 | "list": []
44 | },
45 | "annotations": {
46 | "list": []
47 | },
48 | "refresh": "5s",
49 | "schemaVersion": 17,
50 | "version": 0,
51 | "links": []
52 | }
53 | ---
54 | apiVersion: grafana.integreatly.org/v1beta1
55 | kind: GrafanaDashboard
56 | metadata:
57 | name: grafanadashboard-from-configmap
58 | spec:
59 | instanceSelector:
60 | matchLabels:
61 | dashboards: "grafana"
62 | configMapRef:
63 | name: dashboard-definition
64 | key: json
65 |
--------------------------------------------------------------------------------
/examples/dashboard_from_grafana_com/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Dashboard from grafana.com/dashboards"
3 | linkTitle: "Dashboard from grafana.com/dashboards"
4 | ---
5 |
6 | Shows how to obtain the dashboard definition from [grafana.com/dashboards](https://grafana.com/dashboards).
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/dashboard_from_grafana_com/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | config:
10 | log:
11 | mode: "console"
12 | auth:
13 | disable_login_form: "false"
14 | security:
15 | admin_user: root
16 | admin_password: secret
17 | ---
18 | apiVersion: grafana.integreatly.org/v1beta1
19 | kind: GrafanaDashboard
20 | metadata:
21 | name: node-exporter-latest
22 | spec:
23 | instanceSelector:
24 | matchLabels:
25 | dashboards: "grafana"
26 | grafanaCom:
27 | id: 1860
28 | ---
29 | apiVersion: grafana.integreatly.org/v1beta1
30 | kind: GrafanaDashboard
31 | metadata:
32 | name: instio-control-plane-pinned-revision
33 | spec:
34 | instanceSelector:
35 | matchLabels:
36 | dashboards: "grafana"
37 | grafanaCom:
38 | id: 7645
39 | revision: 161
40 |
--------------------------------------------------------------------------------
/examples/dashboard_from_url/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Dashboard from URL"
3 | linkTitle: "Dashboard from URL"
4 | ---
5 |
6 | Shows how to obtain the dashboard definition (json) from an external url.
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/dashboard_from_url/dashboard.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": null,
3 | "title": "Simple Dashboard-url",
4 | "tags": [],
5 | "style": "dark",
6 | "timezone": "browser",
7 | "editable": true,
8 | "hideControls": false,
9 | "graphTooltip": 1,
10 | "panels": [],
11 | "time": {
12 | "from": "now-6h",
13 | "to": "now"
14 | },
15 | "timepicker": {
16 | "time_options": [],
17 | "refresh_intervals": []
18 | },
19 | "templating": {
20 | "list": []
21 | },
22 | "annotations": {
23 | "list": []
24 | },
25 | "refresh": "5s",
26 | "schemaVersion": 17,
27 | "version": 0,
28 | "links": []
29 | }
30 |
--------------------------------------------------------------------------------
/examples/dashboard_from_url/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | config:
10 | log:
11 | mode: "console"
12 | auth:
13 | disable_login_form: "false"
14 | security:
15 | admin_user: root
16 | admin_password: secret
17 | ---
18 | apiVersion: grafana.integreatly.org/v1beta1
19 | kind: GrafanaDashboard
20 | metadata:
21 | name: grafanadashboard-from-url
22 | spec:
23 | instanceSelector:
24 | matchLabels:
25 | dashboards: "grafana"
26 | url: "https://raw.githubusercontent.com/grafana-operator/grafana-operator/master/examples/dashboard_from_url/dashboard.json"
27 |
--------------------------------------------------------------------------------
/examples/dashboard_gzipped/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Dashboard gzipped"
3 | linkTitle: "Dashboard gzipped"
4 | ---
5 |
6 | Shows how to store the dashboard definition (json) as gzip compressed.
7 |
8 | ```shell
9 | cat dashboard.json | gzip | base64 -w0
10 | ```
11 |
12 | {{< readfile file="dashboard.json" code="true" lang="json" >}}
13 |
14 | Should provide
15 |
16 | ```txt
17 | H4sIAAAAAAAAA4WQQU/DMAyF7/0VVc9MggMgcYV/AOKC0OQubmM1jSPH28Sm/XfSNJ1WcaA3f+/l+dXnqk5fQ6Z5qf3eubt5VlKHCTXvNAaH9RtE2zKI2fQnCgFNsxihj8n39V3mqD/zQwMyXE004ol95q3wMaIsEhpSaPMTlT0WasngK3sVdlN6By4uUi8Q7AezUwpJeig4gEe3ajItTfM5T5l0wuNUwfNx82RLg9nLhTeZXW4iAu2GVHcVNPEtByX2tyuzJtgJRrslrygHKJ3WsZhuCkq+X8c6ivrXDd6zwrLrX3vZP/3PY1yuHHcWR/hEiSlmutpzEQ5XdF+IIz+Uzpeq+gWtMMT1HwIAAA==
18 | ```
19 |
20 | And simply add this output to your dashboard CR
21 |
22 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
23 |
--------------------------------------------------------------------------------
/examples/dashboard_gzipped/dashboard.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": null,
3 | "title": "Simple Dashboard-gzipped",
4 | "tags": [],
5 | "style": "dark",
6 | "timezone": "browser",
7 | "editable": true,
8 | "hideControls": false,
9 | "graphTooltip": 1,
10 | "panels": [],
11 | "time": {
12 | "from": "now-6h",
13 | "to": "now"
14 | },
15 | "timepicker": {
16 | "time_options": [],
17 | "refresh_intervals": []
18 | },
19 | "templating": {
20 | "list": []
21 | },
22 | "annotations": {
23 | "list": []
24 | },
25 | "refresh": "5s",
26 | "schemaVersion": 17,
27 | "version": 0,
28 | "links": []
29 | }
30 |
--------------------------------------------------------------------------------
/examples/dashboard_gzipped/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | config:
10 | log:
11 | mode: "console"
12 | auth:
13 | disable_login_form: "false"
14 | security:
15 | admin_user: root
16 | admin_password: secret
17 | ---
18 | apiVersion: grafana.integreatly.org/v1beta1
19 | kind: GrafanaDashboard
20 | metadata:
21 | name: grafanadashboard-gzipped
22 | spec:
23 | instanceSelector:
24 | matchLabels:
25 | dashboards: "grafana"
26 | gzipJson: |-
27 | H4sIAAAAAAAAA4WQQU/DMAyF7/0VVc9MggMgcYV/AOKC0OQubmM1jSPH28Sm/XfSNJ1WcaA3f+/l+dXnqk5fQ6Z5qf3eubt5VlKHCTXvNAaH9RtE2zKI2fQnCgFNsxihj8n39V3mqD/zQwMyXE004ol95q3wMaIsEhpSaPMTlT0WasngK3sVdlN6By4uUi8Q7AezUwpJeig4gEe3ajItTfM5T5l0wuNUwfNx82RLg9nLhTeZXW4iAu2GVHcVNPEtByX2tyuzJtgJRrslrygHKJ3WsZhuCkq+X8c6ivrXDd6zwrLrX3vZP/3PY1yuHHcWR/hEiSlmutpzEQ5XdF+IIz+Uzpeq+gWtMMT1HwIAAA==
28 |
--------------------------------------------------------------------------------
/examples/dashboard_with_custom_folder/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Custom dashboard folder"
3 | linkTitle: "Custom dashboard folder"
4 | ---
5 |
6 | This example shows how to add assign a dashboard to a folder through the dashboard resource,
7 | avoiding the use of the GrafanaFolder resource.
8 |
9 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
10 |
--------------------------------------------------------------------------------
/examples/dashboard_with_custom_folder/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | client:
10 | preferIngress: true
11 | config:
12 | log:
13 | mode: "console"
14 | auth:
15 | disable_login_form: "false"
16 | security:
17 | admin_user: root
18 | admin_password: secret
19 | ---
20 | apiVersion: grafana.integreatly.org/v1beta1
21 | kind: GrafanaDashboard
22 | metadata:
23 | name: grafanadashboard-with-custom-folder
24 | spec:
25 | folder: "Custom Folder"
26 | instanceSelector:
27 | matchLabels:
28 | dashboards: "grafana"
29 | url: "https://raw.githubusercontent.com/grafana-operator/grafana-operator/master/examples/dashboard_from_url/dashboard.json"
30 |
--------------------------------------------------------------------------------
/examples/dashboard_with_external_dependencies/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Dashboard with external dependencies"
3 | linkTitle: "Dashboard with external dependencies"
4 | ---
5 |
6 | This example shows how to obtain the dashboard definition (json) from provided gzip archived project with dependencies:
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/dashboard_with_external_dependencies/dashboard_project/jsonnetfile.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "dependencies": [
4 | {
5 | "source": {
6 | "git": {
7 | "remote": "https://github.com/grafana/grafonnet.git",
8 | "subdir": "gen/grafonnet-latest"
9 | }
10 | },
11 | "version": "main"
12 | }
13 | ],
14 | "legacyImports": true
15 | }
16 |
--------------------------------------------------------------------------------
/examples/dashboard_with_external_dependencies/dashboard_project/util/datasources.libsonnet:
--------------------------------------------------------------------------------
1 | local g = import './g.libsonnet';
2 | local envs = import './envs.libsonnet';
3 |
4 | local datasource = g.dashboard.annotation.datasource;
5 |
6 | {
7 | base(type, uid):
8 | datasource.withType(type)
9 | + datasource.withUid(uid),
10 |
11 | dashboardDatasource:
12 | self.base('datasource', 'grafana'),
13 |
14 | prometheusDatasource:
15 | self.base(envs.PROMETHEUS_DS_TYPE, envs.PROMETHEUS_DS_UID),
16 | }
17 |
--------------------------------------------------------------------------------
/examples/dashboard_with_external_dependencies/dashboard_project/util/envs.libsonnet:
--------------------------------------------------------------------------------
1 | {
2 | // Prometheus datasource type
3 | PROMETHEUS_DS_TYPE:
4 | 'prometheus',
5 |
6 | // Prometheus datasource uid
7 | PROMETHEUS_DS_UID:
8 | std.extVar('PROMETHEUS_DS_UID'),
9 |
10 | //Target namespace where service is deployed
11 | K8S_NAMESPACE:
12 | std.extVar('K8S_NAMESPACE')
13 | }
14 |
--------------------------------------------------------------------------------
/examples/dashboard_with_external_dependencies/dashboard_project/util/g.libsonnet:
--------------------------------------------------------------------------------
1 | import 'github.com/grafana/grafonnet/gen/grafonnet-latest/main.libsonnet'
2 |
--------------------------------------------------------------------------------
/examples/dashboard_with_external_dependencies/dashboard_project/util/queries.libsonnet:
--------------------------------------------------------------------------------
1 | local datasources = import './datasources.libsonnet';
2 | local g = import './g.libsonnet';
3 | local envs = import './envs.libsonnet';
4 |
5 | local query = g.query;
6 | local prometheusQuery = query.prometheus;
7 |
8 | {
9 | readyPods:
10 | prometheusQuery.new(
11 | envs.PROMETHEUS_DS_UID,
12 | |||
13 | avg by (container) (100 * kube_pod_container_status_ready{namespace="$namespace", pod=~"pod-.*", job="kube-state-metrics"})[1m]
14 | |||
15 | ) + prometheusQuery.withLegendFormat('{{`{{ pod }}`}}'),
16 |
17 | totalRestartsPerContainer:
18 | prometheusQuery.new(
19 | envs.PROMETHEUS_DS_UID,
20 | |||
21 | sum by (container) (kube_pod_container_status_restarts_total{namespace="$namespace", pod=~"pod-.*", job="kube-state-metrics"})
22 | |||
23 | ) + prometheusQuery.withLegendFormat('{{`{{ container }}`}}')
24 | + prometheusQuery.withEditorMode('code'),
25 | }
26 |
--------------------------------------------------------------------------------
/examples/dashboard_with_external_dependencies/dashboard_project/util/variables.libsonnet:
--------------------------------------------------------------------------------
1 | local g = import './g.libsonnet';
2 | local datasources = import './datasources.libsonnet';
3 | local envs = import './envs.libsonnet';
4 |
5 | local var = g.dashboard.variable;
6 |
7 | {
8 | namespace:
9 | var.constant.new('namespace', [envs.K8S_NAMESPACE]),
10 |
11 | promteheus_metrics_aggegation_time:
12 | var.interval.new('agg_time', ['1m', '5m', '10m', '15m', '30m', '1h', '3h', '6h', '12h', '1d', '3d', '1w', '1m'])
13 | + var.interval.generalOptions.withLabel('Metrics aggregation time')
14 | }
15 |
--------------------------------------------------------------------------------
/examples/dashboard_with_external_dependencies/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | config:
10 | log:
11 | mode: "console"
12 | auth:
13 | disable_login_form: "false"
14 | security:
15 | admin_user: root
16 | admin_password: secret
17 | ---
18 | apiVersion: grafana.integreatly.org/v1beta1
19 | kind: GrafanaDashboard
20 | metadata:
21 | name: grafana-dashboard-with-dependencies
22 | spec:
23 | instanceSelector:
24 | matchLabels:
25 | dashboards: "grafana"
26 | envs:
27 | - name: PROMETHEUS_DS_UID
28 | value: "uuid"
29 | - name: K8S_NAMESPACE
30 | value: "k8s_namespace"
31 | - name: DASHBOARD_JSONNET_RECURSIVE
32 | value: "true"
33 | jsonnetLib:
34 | jPath:
35 | - "vendor"
36 | fileName: "dashboard.jsonnet"
37 | gzipJsonnetProject: |-
38 | {{- (.Files.Get "dashboards.tar.gz") | b64enc | nindent 6 }}
39 |
--------------------------------------------------------------------------------
/examples/datasource_mapping/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Datasource mapping"
3 | linkTitle: "Datasource mapping"
4 | ---
5 |
6 | This example shows how to map an internal data source to an input from an exported dashboard.
7 |
8 | There are two datasources in this example.
9 | This to visualize that we can use multiple datasources and specify which `datasourceName` to use when overwriting the `DS_PROMETHEUS` config in the grafana dashboard json.
10 |
11 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
12 |
--------------------------------------------------------------------------------
/examples/datasource_types/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Datasource types"
3 | linkTitle: "Datasource types"
4 | ---
5 |
6 | This example shows different types of datasources and how to configure them.
7 |
8 | {{< readfile file="prometheus.yaml" code="true" lang="yaml" >}}
9 | {{< readfile file="postgresql.yaml" code="true" lang="yaml" >}}
10 |
--------------------------------------------------------------------------------
/examples/datasource_types/postgresql.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDatasource
3 | metadata:
4 | name: postgresql
5 | namespace: grafana
6 | spec:
7 | instanceSelector:
8 | matchLabels:
9 | dashboards: "grafana"
10 | datasource:
11 | name: postgresql
12 | type: postgres
13 | jsonData:
14 | database: postgres
15 | connMaxLifetime: 14400
16 | maxIdleConns: 2
17 | maxOpenConns: 0
18 | postgresVersion: 1400
19 | sslmode: disable
20 | timescaledb: false
21 | access: proxy
22 | secureJsonData:
23 | password: postgres
24 | url: postgresql.namespace.svc:5432
25 | user: postgres
26 |
--------------------------------------------------------------------------------
/examples/datasource_types/prometheus.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDatasource
3 | metadata:
4 | name: prometheus
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | dashboards: "grafana"
9 | datasource:
10 | name: prom1
11 | type: prometheus
12 | access: proxy
13 | url: http://prometheus-service:9090
14 | isDefault: true
15 | jsonData:
16 | "tlsSkipVerify": true
17 | "timeInterval": "5s"
18 |
--------------------------------------------------------------------------------
/examples/datasource_variables/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Datasource variable"
3 | linkTitle: "Datasource variable"
4 | ---
5 |
6 | This example shows how to reference values from a secret in data source fields.
7 | Values can be assigned using `spec.valuesFrom`:
8 |
9 | ```yaml
10 | valuesFrom:
11 | - targetPath: "secureJsonData.httpHeaderValue1"
12 | valueFrom:
13 | secretKeyRef:
14 | name: "credentials"
15 | key: "PROMETHEUS_TOKEN"
16 | ```
17 |
18 | The Operator will look for a key with the name `PROMETHEUS_TOKEN` in a secret with the name `credentials`.
19 | It will then inject the value into `secureJsonData.httpHeaderValue1`:
20 |
21 | ```yaml
22 | datasource:
23 | secureJsonData:
24 | "httpHeaderValue1": "Bearer ${PROMETHEUS_TOKEN}"
25 | ```
26 |
27 | The Operator expects a string to be present with the replacement pattern.
28 |
29 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
30 |
--------------------------------------------------------------------------------
/examples/datasource_variables/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | config:
10 | log:
11 | mode: "console"
12 | auth:
13 | disable_login_form: "false"
14 | security:
15 | admin_user: root
16 | admin_password: secret
17 | ---
18 | kind: Secret
19 | apiVersion: v1
20 | metadata:
21 | name: credentials
22 | namespace: grafana
23 | stringData:
24 | PROMETHEUS_TOKEN: secret_token
25 | type: Opaque
26 | ---
27 | apiVersion: grafana.integreatly.org/v1beta1
28 | kind: GrafanaDatasource
29 | metadata:
30 | name: grafanadatasource-sample
31 | spec:
32 | valuesFrom:
33 | - targetPath: "secureJsonData.httpHeaderValue1"
34 | valueFrom:
35 | secretKeyRef:
36 | name: "credentials"
37 | key: "PROMETHEUS_TOKEN"
38 | instanceSelector:
39 | matchLabels:
40 | dashboards: "grafana"
41 | datasource:
42 | name: prometheus
43 | type: prometheus
44 | access: proxy
45 | basicAuth: true
46 | url: http://prometheus-service:9090
47 | isDefault: true
48 | jsonData:
49 | "tlsSkipVerify": true
50 | "timeInterval": "5s"
51 | httpHeaderName1: "Authorization"
52 | secureJsonData:
53 | "httpHeaderValue1": "Bearer ${PROMETHEUS_TOKEN}"
54 |
--------------------------------------------------------------------------------
/examples/external_grafana/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "External grafana"
3 | linkTitle: "External grafana"
4 | ---
5 | A basic example configuring an external grafana instance.
6 |
7 | In this case we create a grafana instance through the operator just to showcase that it can be done.
8 |
9 | Notice the `instanceSelector` of the grafana dashboard and the grafana instances.
10 | You will notice that it's only the grafana instance called `external-grafana` that will match with the grafana dashboard called `external-grafanadashboard-sample`.
11 |
12 | If you look in the status of external-grafana you will also see the hash of the dashboard that have been applied.
13 |
14 | ```shell
15 | kubectl get grafanas.grafana.integreatly.org external-grafana -o yaml
16 | # redacted output
17 | status:
18 | adminUrl: http://test.io
19 | dashboards:
20 | - grafana-operator-system/external-grafanadashboard-sample/cb1688d2-547a-465b-bc49-df3ccf3da883
21 | stage: complete
22 | stageStatus: success
23 | ```
24 |
25 | ## FYI
26 |
27 | If you want run the same test locally on your computer remember to ether update the ingres host to match your settings or change `client.preferIngress` to false assuming that you run the operator within the cluster.
28 |
29 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
30 |
--------------------------------------------------------------------------------
/examples/folder/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Folder with permissions"
3 | linkTitle: "Folder with permissions"
4 | ---
5 |
6 | Shows how to create a folder with custom title and [permissions](https://grafana.com/docs/grafana/v8.4/http_api/folder_permissions/#update-permissions-for-a-folder).
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/folder/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | config:
10 | log:
11 | mode: "console"
12 | auth:
13 | disable_login_form: "false"
14 | security:
15 | admin_user: root
16 | admin_password: secret
17 | ---
18 | apiVersion: grafana.integreatly.org/v1beta1
19 | kind: GrafanaFolder
20 | metadata:
21 | name: test-folder
22 | spec:
23 | instanceSelector:
24 | matchLabels:
25 | dashboards: "grafana"
26 |
27 | # If title is not defined, the value will be taken from metadata.name
28 | title: custom title
29 |
30 | # When permissions value is empty/absent, a folder is created with default permissions
31 | # When empty JSON is passed ("{}"), the access is stripped for everyone except for Admin (default Grafana behaviour)
32 | permissions: |
33 | {
34 | "items": [
35 | {
36 | "role": "Admin",
37 | "permission": 4
38 | },
39 | {
40 | "role": "Editor",
41 | "permission": 2
42 | }
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/examples/grafana_deployment/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Grafana deployment config"
3 | linkTitle: "Grafana deployment config"
4 | ---
5 |
6 | A basic deployment of Grafana that overrides the existing image, readiness and securityContext.
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/grafana_deployment/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | config:
10 | security:
11 | admin_user: root
12 | admin_password: secret
13 | deployment:
14 | spec:
15 | template:
16 | spec:
17 | containers:
18 | - name: grafana
19 | securityContext:
20 | # Customize this in case your volume provider needs specific UIDs/GIDs
21 | runAsUser: 1001
22 | runAsGroup: 1001
23 | runAsNonRoot: true
24 | allowPrivilegeEscalation: false
25 | capabilities:
26 | drop: ["ALL"]
27 | readinessProbe:
28 | failureThreshold: 3
29 |
--------------------------------------------------------------------------------
/examples/ingress_http/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Ingress http"
3 | linkTitle: "Ingress http"
4 | ---
5 |
6 | A very basic ingress that should only be used for testing.
7 | Always always use https.
8 |
9 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
10 |
--------------------------------------------------------------------------------
/examples/ingress_http/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | config:
10 | log:
11 | mode: "console"
12 | auth:
13 | disable_login_form: "false"
14 | security:
15 | admin_user: root
16 | admin_password: secret
17 | ingress:
18 | spec:
19 | ingressClassName: nginx
20 | rules:
21 | - host: test.io
22 | http:
23 | paths:
24 | - backend:
25 | service:
26 | name: grafana-service
27 | port:
28 | number: 3000
29 | path: /
30 | pathType: Prefix
31 |
--------------------------------------------------------------------------------
/examples/ingress_https/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Ingress https"
3 | linkTitle: "Ingress https"
4 | ---
5 | Assumes that you have [cert-manager](https://github.com/cert-manager/cert-manager) running in your cluster and have a ClusterIssuer called letsencrypt.
6 |
7 | It also assumes that you have `ingressClassName: nginx`.
8 |
9 | You can of course have added a certificate to a secret manually.
10 |
11 | {{< readfile file="cert.yaml" code="true" lang="yaml" >}}
12 |
13 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
14 |
15 | {{< readfile file="dashboard.yaml" code="true" lang="yaml" >}}
16 |
--------------------------------------------------------------------------------
/examples/ingress_https/cert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1
2 | kind: Certificate
3 | metadata:
4 | name: cert
5 | spec:
6 | issuerRef:
7 | group: cert-manager.io
8 | kind: ClusterIssuer
9 | name: letsencrypt
10 | dnsNames:
11 | - example.com
12 | secretName: core-cert
13 | revisionHistoryLimit: 1
14 |
--------------------------------------------------------------------------------
/examples/ingress_https/dashboard.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: GrafanaDashboard
4 | metadata:
5 | name: grafana-dashboard
6 | spec:
7 | instanceSelector:
8 | matchLabels:
9 | dashboards: "grafana"
10 | json: >
11 | {
12 | "id": null,
13 | "title": "Simple Dashboard",
14 | "tags": [],
15 | "style": "dark",
16 | "timezone": "browser",
17 | "editable": true,
18 | "hideControls": false,
19 | "graphTooltip": 1,
20 | "panels": [],
21 | "time": {
22 | "from": "now-6h",
23 | "to": "now"
24 | },
25 | "timepicker": {
26 | "time_options": [],
27 | "refresh_intervals": []
28 | },
29 | "templating": {
30 | "list": []
31 | },
32 | "annotations": {
33 | "list": []
34 | },
35 | "refresh": "5s",
36 | "schemaVersion": 17,
37 | "version": 0,
38 | "links": []
39 | }
40 |
--------------------------------------------------------------------------------
/examples/ingress_https/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | config:
10 | log:
11 | mode: "console"
12 | auth:
13 | disable_login_form: "false"
14 | security:
15 | admin_user: root
16 | admin_password: secret
17 | ingress:
18 | spec:
19 | ingressClassName: nginx
20 | rules:
21 | - host: example.com
22 | http:
23 | paths:
24 | - backend:
25 | service:
26 | name: grafana-service
27 | port:
28 | number: 3000
29 | path: /
30 | pathType: Prefix
31 | tls:
32 | - hosts:
33 | - example.com
34 | secretName: core-cert
35 |
--------------------------------------------------------------------------------
/examples/jsonnet/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Jsonnet"
3 | linkTitle: "Jsonnet"
4 | ---
5 |
6 | A basic deployment of Grafana with a dashboard fom jsonnet.
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/k3d/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "k3d example"
3 | linkTitle: "k3d example"
4 | ---
5 |
6 | A basic deployment of Grafana in k3d/k3d making use of the built-in Ingress controller.
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/ldap/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "LDAP configmap auth"
3 | linkTitle: "LDAP configmap auth"
4 | ---
5 |
6 | A basic example of a Grafana Deployment with LDAP integration. The LDAP integration in Grafana allows your Grafana users to login with their LDAP credentials
7 | For cusomize you ldap-config configmap read [this](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/ldap/)
8 |
9 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
10 |
--------------------------------------------------------------------------------
/examples/multiple_replicas/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Multiple replicas"
3 | linkTitle: "Multiple replicas"
4 | ---
5 |
6 | This example shows how to run multiple replicas of Grafana sharing a PostgreSQL database.
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/mute_timing/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Mute timing"
3 | linkTitle: "Mute timing"
4 | ---
5 |
6 | Shows how to create a mute timing.
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/mute_timing/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: GrafanaMuteTiming
4 | metadata:
5 | name: mutetiming-sample
6 | spec:
7 | instanceSelector:
8 | matchLabels:
9 | dashboards: "grafana"
10 | name: mutetiming-sample
11 | editable: false
12 | time_intervals:
13 | - times:
14 | - start_time: "00:00"
15 | end_time: "06:00"
16 | weekdays: ["saturday", "sunday"]
17 | days_of_month: ["1", "10"]
18 | location: "Asia/Shanghai"
19 |
--------------------------------------------------------------------------------
/examples/notification-policy/resources.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaNotificationPolicy
3 | metadata:
4 | name: grafananotificationpolicy-sample
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | dashboards: "grafana"
9 | route:
10 | receiver: grafana-email-default
11 | group_by:
12 | - grafana_folder
13 | - alertname
14 | routes:
15 | - receiver: grafana-email-operations
16 | object_matchers:
17 | - - team
18 | - =
19 | - operations
20 | - receiver: grafana-email-security
21 | object_matchers:
22 | - - team
23 | - =
24 | - security
25 |
--------------------------------------------------------------------------------
/examples/notification_template/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Notification template"
3 | linkTitle: "Notification template"
4 | ---
5 |
6 | Shows how to create a notification template.
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/notification_template/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: GrafanaNotificationTemplate
4 | metadata:
5 | name: notificationtemplate-sample
6 | spec:
7 | instanceSelector:
8 | matchLabels:
9 | dashboards: "grafana"
10 | name: notificationtemplate-sample
11 | editable: false
12 | template: |
13 | {{ define "SlackAlert" }}
14 | [{{.Status}}] {{ .Labels.alertname }}
15 | {{ .Annotations.AlertValues }}
16 | {{ end }}
17 |
18 | {{ define "SlackAlertMessage" }}
19 | {{ if gt (len .Alerts.Firing) 0 }}
20 | {{ len .Alerts.Firing }} firing:
21 | {{ range .Alerts.Firing }} {{ template "SlackAlert" . }} {{ end }}
22 | {{ end }}
23 | {{ if gt (len .Alerts.Resolved) 0 }}
24 | {{ len .Alerts.Resolved }} resolved:
25 | {{ range .Alerts.Resolved }} {{ template "SlackAlert" . }} {{ end }}
26 | {{ end }}
27 | {{ end }}
28 |
--------------------------------------------------------------------------------
/examples/notifications-full/contact-points.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: GrafanaContactPoint
4 | metadata:
5 | name: operations-team
6 | spec:
7 | name: operations-team
8 | type: "email"
9 | instanceSelector:
10 | matchLabels:
11 | instance: my-grafana-stack
12 | settings:
13 | addresses: 'operations@example.com'
14 | ---
15 | apiVersion: grafana.integreatly.org/v1beta1
16 | kind: GrafanaContactPoint
17 | metadata:
18 | name: security-team
19 | spec:
20 | name: security-team
21 | type: "email"
22 | instanceSelector:
23 | matchLabels:
24 | instance: my-grafana-stack
25 | settings:
26 | addresses: 'security@example.com'
27 |
--------------------------------------------------------------------------------
/examples/notifications-full/folder.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaFolder
3 | metadata:
4 | name: alerts-demo
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | instance: "my-grafana-stack"
9 |
--------------------------------------------------------------------------------
/examples/notifications-full/notification-policy.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: GrafanaNotificationPolicy
4 | metadata:
5 | name: test
6 | spec:
7 | instanceSelector:
8 | matchLabels:
9 | instance: "my-grafana-stack"
10 | route:
11 | receiver: grafana-default-email
12 | group_by:
13 | - grafana_folder
14 | - alertname
15 | routes:
16 | - receiver: operations-team
17 | object_matchers:
18 | - - team
19 | - =
20 | - operations
21 | routes:
22 | - object_matchers:
23 | - - severity
24 | - =
25 | - high
26 | repeat_interval: 5m
27 | - receiver: security-team
28 | object_matchers:
29 | - - team
30 | - =
31 | - security
32 |
--------------------------------------------------------------------------------
/examples/oauth_proxy/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Oauth proxy"
3 | linkTitle: "Oauth proxy"
4 | ---
5 | An example that uses an auth proxy to enforce authentication.
6 |
7 | NOTE: this example currently only works on OpenShift
8 |
9 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
10 |
--------------------------------------------------------------------------------
/examples/openshift/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "OpenShift example"
3 | linkTitle: "OpenShift example"
4 | ---
5 |
6 | A basic deployment that makes use of OpenShift routes.
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/openshift/resources.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: Grafana
3 | metadata:
4 | name: grafana
5 | labels:
6 | dashboards: "grafana"
7 | spec:
8 | # An empty route spec is enough to signal the creation of a default
9 | # route to the operator. This can also be used to override defaults
10 | # in the route spec.
11 | route:
12 | spec: {}
13 | config:
14 | log:
15 | mode: "console"
16 | auth:
17 | disable_login_form: "false"
18 | security:
19 | admin_user: root
20 | admin_password: secret
21 |
--------------------------------------------------------------------------------
/examples/persistent_volume/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Grafana deployment with a Persistent Volume"
3 | linkTitle: "Grafana deployment with a Persistent Volume"
4 | ---
5 |
6 | A basic deployment of Grafana with a persistent volume attached using existing Storage Class.
7 |
8 | {{< readfile file="resources.yaml" code="true" lang="yaml" >}}
9 |
--------------------------------------------------------------------------------
/examples/persistent_volume/resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana
6 | labels:
7 | dashboards: "grafana"
8 | spec:
9 | persistentVolumeClaim:
10 | spec:
11 | accessModes:
12 | - ReadWriteOnce
13 | resources:
14 | requests:
15 | storage: 10Gi
16 | # storageClassName: "" # Customize storage class if needed
17 | config:
18 | log:
19 | mode: "console"
20 | auth:
21 | disable_login_form: "false"
22 | security:
23 | admin_user: root
24 | admin_password: secret
25 | deployment:
26 | spec:
27 | template:
28 | spec:
29 | containers:
30 | - name: grafana
31 | readinessProbe:
32 | failureThreshold: 3
33 | volumes:
34 | - name: grafana-data
35 | persistentVolumeClaim:
36 | claimName: grafana-pvc
37 | strategy:
38 | type: Recreate
39 |
--------------------------------------------------------------------------------
/examples/plugins/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Grafana plugins"
3 | linkTitle: "Grafana plugins"
4 | ---
5 | This won't work on external grafana instances.
6 | Due to the operator don't own and thus we can't set the environment variable that we use to make grafana install plugins for us.
7 |
8 | {{< readfile file="dashboard.yaml" code="true" lang="yaml" >}}
9 | {{< readfile file="datasource.yaml" code="true" lang="yaml" >}}
10 |
--------------------------------------------------------------------------------
/examples/plugins/datasource.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDatasource
3 | metadata:
4 | name: example-grafanadatasource
5 | spec:
6 | datasource:
7 | access: proxy
8 | type: prometheus
9 | jsonData:
10 | timeInterval: 5s
11 | tlsSkipVerify: true
12 | name: Prometheus
13 | url: http://prometheus-service:9090
14 | instanceSelector:
15 | matchLabels:
16 | dashboards: grafana
17 | plugins:
18 | - name: grafana-clock-panel
19 | version: 1.3.0
20 |
--------------------------------------------------------------------------------
/hack/add-openshift-annotations.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | OPENSHIFT_VERSIONS="\"v4.11\""
4 |
5 | {
6 | echo ""
7 | echo " # OpenShift specific annotations"
8 | echo " com.redhat.openshift.versions: $OPENSHIFT_VERSIONS"
9 | } >> bundle/metadata/annotations.yaml
10 |
11 | echo "LABEL com.redhat.openshift.versions=$OPENSHIFT_VERSIONS" >> bundle.Dockerfile
12 |
--------------------------------------------------------------------------------
/hack/boilerplate.go.txt:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
--------------------------------------------------------------------------------
/hack/kind/resources/cluster.yaml:
--------------------------------------------------------------------------------
1 | kind: Cluster
2 | apiVersion: kind.x-k8s.io/v1alpha4
3 | nodes:
4 | - role: control-plane
5 | kubeadmConfigPatches:
6 | - |
7 | kind: InitConfiguration
8 | nodeRegistration:
9 | kubeletExtraArgs:
10 | node-labels: "ingress-ready=true"
11 | extraPortMappings:
12 | - containerPort: 80
13 | hostPort: 80
14 | protocol: TCP
15 | - containerPort: 443
16 | hostPort: 443
17 | protocol: TCP
18 |
--------------------------------------------------------------------------------
/hack/kind/resources/crd-ns/grafana-dashboard.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDashboard
3 | metadata:
4 | name: grafana
5 | labels:
6 | dashboards: "grafana"
7 | spec:
8 | folder: my-folder-name
9 | instanceSelector:
10 | matchLabels:
11 | dashboards: "grafana"
12 | json: |
13 | {
14 | "id": null,
15 | "title": "Simple Dashboard in CRD NS",
16 | "tags": [],
17 | "style": "dark",
18 | "timezone": "browser",
19 | "editable": true,
20 | "hideControls": false,
21 | "graphTooltip": 1,
22 | "panels": [],
23 | "time": {
24 | "from": "now-6h",
25 | "to": "now"
26 | },
27 | "timepicker": {
28 | "time_options": [],
29 | "refresh_intervals": []
30 | },
31 | "templating": {
32 | "list": []
33 | },
34 | "annotations": {
35 | "list": []
36 | },
37 | "refresh": "5s",
38 | "schemaVersion": 17,
39 | "version": 0,
40 | "links": []
41 | }
42 |
--------------------------------------------------------------------------------
/hack/kind/resources/crd-ns/grafana-datasource.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDatasource
3 | metadata:
4 | name: example-grafanadatasource
5 | spec:
6 | datasource:
7 | access: proxy
8 | type: prometheus
9 | jsonData:
10 | timeInterval: 5s
11 | tlsSkipVerify: true
12 | name: Prometheus-in-crd-nd
13 | url: http://prometheus-service:9090
14 | instanceSelector:
15 | matchLabels:
16 | dashboards: grafana
17 | plugins:
18 | - name: grafana-clock-panel
19 | version: 1.3.0
20 |
--------------------------------------------------------------------------------
/hack/kind/resources/crd-ns/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - grafana-dashboard.yaml
3 | - grafana-datasource.yaml
4 |
--------------------------------------------------------------------------------
/hack/kind/resources/default/deployment-thanos.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: thanos
5 | labels:
6 | app: thanos
7 | spec:
8 | selector:
9 | matchLabels:
10 | app: thanos
11 | template:
12 | metadata:
13 | labels:
14 | app: thanos
15 | spec:
16 | terminationGracePeriodSeconds: 3
17 | containers:
18 | - name: netcat
19 | image: alpine
20 | command:
21 | - sh
22 | - -c
23 | - |
24 | set -eu
25 | echo "Starting pod"
26 | while true; do echo -e 'HTTP/1.1 200 OK\n\n{"asdf":"date"}' | nc -l -p 8080; done
27 | ports:
28 | - containerPort: 8080
29 | name: http
30 | protocol: TCP
31 | ---
32 | apiVersion: v1
33 | kind: Service
34 | metadata:
35 | name: thanos
36 | spec:
37 | selector:
38 | app: thanos
39 | ports:
40 | - port: 8080
41 | name: http
42 | protocol: TCP
43 | targetPort: 8080
44 |
--------------------------------------------------------------------------------
/hack/kind/resources/default/grafana-contactpoint.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaContactPoint
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: grafanacontactpoint
6 | app.kubernetes.io/instance: grafanacontactpoint-sample
7 | app.kubernetes.io/part-of: grafana-operator
8 | app.kubernetes.io/managed-by: kustomize
9 | app.kubernetes.io/created-by: grafana-operator
10 | name: grafanacontactpoint-sample
11 | spec:
12 | name: grafanacontactpoint-sample
13 | type: "email"
14 | instanceSelector:
15 | matchLabels:
16 | dashboards: "grafana"
17 | settings:
18 | addresses: "email@email.com"
19 |
--------------------------------------------------------------------------------
/hack/kind/resources/default/grafana-dashboard.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDashboard
3 | metadata:
4 | name: grafana
5 | labels:
6 | dashboards: "grafana"
7 | spec:
8 | folder: my-folder-name
9 | instanceSelector:
10 | matchLabels:
11 | dashboards: "grafana"
12 | json: |
13 | {
14 | "id": null,
15 | "title": "Simple Dashboard",
16 | "tags": [],
17 | "style": "dark",
18 | "timezone": "browser",
19 | "editable": true,
20 | "hideControls": false,
21 | "graphTooltip": 1,
22 | "panels": [],
23 | "time": {
24 | "from": "now-6h",
25 | "to": "now"
26 | },
27 | "timepicker": {
28 | "time_options": [],
29 | "refresh_intervals": []
30 | },
31 | "templating": {
32 | "list": []
33 | },
34 | "annotations": {
35 | "list": []
36 | },
37 | "refresh": "5s",
38 | "schemaVersion": 17,
39 | "version": 0,
40 | "links": []
41 | }
42 |
--------------------------------------------------------------------------------
/hack/kind/resources/default/grafana-datasource-thanos.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDatasource
3 | metadata:
4 | name: thanos
5 | spec:
6 | datasource:
7 | access: proxy
8 | basicAuth: false
9 | isDefault: true
10 | jsonData:
11 | httpHeaderName1: Authorization
12 | timeInterval: 5s
13 | tlsSkipVerify: true
14 | name: Thanos
15 | secureJsonData:
16 | httpHeaderValue1: Bearer ${token}
17 | type: prometheus
18 | url: http://thanos.default.svc:8080
19 | instanceSelector:
20 | matchLabels:
21 | dashboards: grafana
22 | valuesFrom:
23 | - targetPath: "secureJsonData.httpHeaderValue1"
24 | valueFrom:
25 | secretKeyRef:
26 | name: grafana-instance-sa-token
27 | key: token
28 |
--------------------------------------------------------------------------------
/hack/kind/resources/default/grafana-datasource.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDatasource
3 | metadata:
4 | name: example-grafanadatasource
5 | spec:
6 | datasource:
7 | access: proxy
8 | type: prometheus
9 | jsonData:
10 | timeInterval: 5s
11 | tlsSkipVerify: true
12 | name: Prometheus
13 | url: http://prometheus-service:9090
14 | instanceSelector:
15 | matchLabels:
16 | dashboards: grafana
17 | plugins:
18 | - name: grafana-clock-panel
19 | version: 1.3.0
20 |
--------------------------------------------------------------------------------
/hack/kind/resources/default/grafana.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: Grafana
3 | metadata:
4 | name: grafana
5 | labels:
6 | dashboards: "grafana"
7 | spec:
8 | client:
9 | preferIngress: true
10 | config:
11 | log:
12 | mode: "console"
13 | feature_toggles:
14 | enable: nestedFolders
15 | auth:
16 | disable_login_form: "false"
17 | security:
18 | admin_user: root
19 | admin_password: secret
20 | ingress:
21 | spec:
22 | ingressClassName: nginx
23 | rules:
24 | - host: grafana.127.0.0.1.nip.io
25 | http:
26 | paths:
27 | - backend:
28 | service:
29 | name: grafana-service
30 | port:
31 | number: 3000
32 | path: /
33 | pathType: Prefix
34 |
--------------------------------------------------------------------------------
/hack/kind/resources/default/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - grafana.yaml
3 | - grafana-dashboard.yaml
4 | - grafana-datasource.yaml
5 | - grafana-contactpoint.yaml
6 | - grafana-notification-policy.yaml
7 |
8 | - grafana-datasource-thanos.yaml
9 | - secret.yaml
10 | - serviceaccount.yaml
11 | - deployment-thanos.yaml
12 |
--------------------------------------------------------------------------------
/hack/kind/resources/default/secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: grafana-instance-sa-token
5 | annotations:
6 | kubernetes.io/service-account.name: grafana-instance-sa
7 | type: kubernetes.io/service-account-token
8 |
--------------------------------------------------------------------------------
/hack/kind/resources/default/serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | name: grafana-instance-sa
5 | secrets:
6 | - name: grafana-instance-sa-token
7 |
--------------------------------------------------------------------------------
/hack/kind/start-kind.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | KIND=${KIND:-kind}
4 | KIND_NODE_VERSION=${KIND_NODE_VERSION:-v1.33.0}
5 | KIND_CLUSTER_NAME=${KIND_CLUSTER_NAME:-kind-grafana}
6 | KUBECONFIG=${KUBECONFIG:-~/.kube/kind-grafana-operator}
7 | set -eu
8 |
9 | # Find the script directory
10 | SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
11 |
12 | # Check if named kind cluster already exists
13 | if [[ "$($KIND get clusters)" =~ "$KIND_CLUSTER_NAME" ]]; then
14 | exit 0
15 | fi
16 |
17 | # Start kind cluster
18 | echo ""
19 | echo "###############################"
20 | echo "# 1. Start kind cluster #"
21 | echo "###############################"
22 | ${KIND} --kubeconfig "${KUBECONFIG}" create cluster \
23 | --name "${KIND_CLUSTER_NAME}" \
24 | --wait 120s \
25 | --config="${SCRIPT_DIR}/resources/cluster.yaml" \
26 | --image="kindest/node:${KIND_NODE_VERSION}"
27 |
--------------------------------------------------------------------------------
/hugo/.gitignore:
--------------------------------------------------------------------------------
1 | /public
2 | resources/
3 | node_modules/
4 | .hugo_build.lock
5 |
--------------------------------------------------------------------------------
/hugo/Makefile:
--------------------------------------------------------------------------------
1 | # Setting SHELL to bash allows bash commands to be executed by recipes.
2 | # Options are set to exit when a recipe line exits non-zero or a piped command fails.
3 | SHELL = /usr/bin/env bash -o pipefail
4 | .SHELLFLAGS = -ec
5 |
6 | DOCS_URL ?= http://localhost:1313
7 |
8 | .PHONY: detect-broken-links
9 | detect-broken-links:
10 | go install github.com/raviqqe/muffet/v2@latest
11 | muffet --rate-limit=3 \
12 | --max-connections=2 \
13 | --buffer-size=8192 \
14 | --exclude=github.com \
15 | --exclude=grafana.127.0.0.1.nip.io \
16 | $(DOCS_URL)
17 |
--------------------------------------------------------------------------------
/hugo/README.md:
--------------------------------------------------------------------------------
1 | # Hugo
2 |
3 | ## Installing and running Hugo
4 |
5 | When installing hugo install the `extended` edition at version `v0.134.0`. You
6 | can find it on the [Hugo release
7 | page](https://github.com/gohugoio/hugo/releases/tag/v0.134.0) or build it with a
8 | working Go toolchain:
9 |
10 | ```shell
11 | CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@v0.134.0
12 | ```
13 |
14 | To develop locally you need to also follow the docsy [pre-req](https://github.com/google/docsy#prerequisites).
15 | But in most cases it should work to just run
16 |
17 | ```shell
18 | cd hugo
19 | # Install npm dependencies
20 | npm ci
21 | # Download hugo module
22 | hugo mod get
23 | ```
24 |
25 | To look at your changes with hot reload.
26 |
27 | ```shell
28 | hugo serve
29 | ```
30 |
31 | ## Detecting broken links in documentation
32 |
33 | To detect broken links in documentation using [muffet](https://github.com/raviqqe/muffet):
34 |
35 | 1. Make sure that hugo is running and serving documentation as specified in the abbove steps.
36 | 2. Run the following command in the hugo directory:
37 |
38 | ```shell
39 | make detect-broken-links
40 | ```
41 |
--------------------------------------------------------------------------------
/hugo/archetypes/default.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "{{ replace .Name "-" " " | title }}"
3 | date: {{ .Date }}
4 | draft: true
5 | ---
6 |
--------------------------------------------------------------------------------
/hugo/assets/scss/_styles_project.scss:
--------------------------------------------------------------------------------
1 | @import 'td/code-dark'
2 |
--------------------------------------------------------------------------------
/hugo/assets/scss/_variables_project.scss:
--------------------------------------------------------------------------------
1 | $primary: #fd7e14;
2 |
--------------------------------------------------------------------------------
/hugo/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/grafana/grafana-operator/hugo
2 |
3 | go 1.19
4 |
5 | require (
6 | github.com/google/docsy v0.11.0 // indirect
7 | github.com/google/docsy/dependencies v0.7.2 // indirect
8 | )
9 |
--------------------------------------------------------------------------------
/hugo/go.sum:
--------------------------------------------------------------------------------
1 | github.com/FortAwesome/Font-Awesome v0.0.0-20230327165841-0698449d50f2/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo=
2 | github.com/FortAwesome/Font-Awesome v0.0.0-20240716171331-37eff7fa00de/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo=
3 | github.com/google/docsy v0.11.0 h1:QnV40cc28QwS++kP9qINtrIv4hlASruhC/K3FqkHAmM=
4 | github.com/google/docsy v0.11.0/go.mod h1:hGGW0OjNuG5ZbH5JRtALY3yvN8ybbEP/v2iaK4bwOUI=
5 | github.com/google/docsy/dependencies v0.7.2 h1:+t5ufoADQAj4XneFphz4A+UU0ICAxmNaRHVWtMYXPSI=
6 | github.com/google/docsy/dependencies v0.7.2/go.mod h1:gihhs5gmgeO+wuoay4FwOzob+jYJVyQbNaQOh788lD4=
7 | github.com/twbs/bootstrap v5.2.3+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0=
8 | github.com/twbs/bootstrap v5.3.3+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0=
9 |
--------------------------------------------------------------------------------
/hugo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "autoprefixer": "^10.4.13",
4 | "postcss": "^8.4.21",
5 | "postcss-cli": "^10.1.0"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/labs/audit/README.md:
--------------------------------------------------------------------------------
1 | # Audit stats lab
2 |
3 | The goal of the lab is to get stats for interactions with kube-apiserver.
4 |
5 | ## Technical requirements
6 |
7 | - bash
8 | - docker
9 | - jq
10 | - kind
11 |
12 | ## Instructions
13 |
14 | Create kind cluster:
15 |
16 | ```shell
17 | kind create cluster --config kind-config.yaml
18 | ```
19 |
20 | Deploy the operator and related resources from the root of the git folder:
21 |
22 | ```shell
23 | export IMG=
24 | make install
25 | make deploy
26 | kubectl apply -f config/samples/grafana_v1beta1_grafana.yaml
27 | kubectl apply -f config/samples/grafana_v1beta1_grafanadashboard.yaml
28 | kubectl apply -f config/samples/grafana_v1beta1_grafanadatasource.yaml
29 | ```
30 |
31 | NOTE: at the time of writing, kubebuilder lacks full definition for cluster-scope RBAC, you need to take care of that.
32 |
33 | Collect stats after a few minutes:
34 |
35 | ```shell
36 | ./collect-audit-stats.sh
37 | ```
38 |
39 | Example:
40 |
41 | ```shell
42 | ./collect-audit-stats.sh
43 | 3 "get - /api/v1/namespaces/grafana-operator-system/configmaps/f75f3bba.integreatly.org"
44 | 6 "get - /apis/coordination.k8s.io/v1/namespaces/grafana-operator-system/leases/f75f3bba.integreatly.org"
45 | 3 "update - /api/v1/namespaces/grafana-operator-system/configmaps/f75f3bba.integreatly.org"
46 | 3 "update - /apis/coordination.k8s.io/v1/namespaces/grafana-operator-system/leases/f75f3bba.integreatly.org"
47 | ```
48 |
--------------------------------------------------------------------------------
/labs/audit/audit-policy.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: audit.k8s.io/v1
2 | kind: Policy
3 | rules:
4 | - level: Metadata
5 | namespaces: ["grafana-operator-system"]
6 |
--------------------------------------------------------------------------------
/labs/audit/collect-audit-stats.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | docker cp kind-control-plane:/var/log/kubernetes/kube-apiserver-audit.log .
5 | docker exec kind-control-plane truncate -s 0 /var/log/kubernetes/kube-apiserver-audit.log
6 | grep RequestReceived kube-apiserver-audit.log | grep "system:serviceaccount:grafana-operator-system:grafana-operator-controller-manager" | jq '. | { verb, requestURI } | join(" - ")' | sort | uniq -c
7 |
--------------------------------------------------------------------------------
/labs/audit/kind-config.yaml:
--------------------------------------------------------------------------------
1 | kind: Cluster
2 | apiVersion: kind.x-k8s.io/v1alpha4
3 | nodes:
4 | - role: control-plane
5 | kubeadmConfigPatches:
6 | - |
7 | kind: ClusterConfiguration
8 | apiServer:
9 | # enable auditing flags on the API server
10 | extraArgs:
11 | audit-log-path: /var/log/kubernetes/kube-apiserver-audit.log
12 | audit-log-maxsize: "100"
13 | audit-policy-file: /etc/kubernetes/policies/audit-policy.yaml
14 | # mount new files / directories on the control plane
15 | extraVolumes:
16 | - name: audit-policies
17 | hostPath: /etc/kubernetes/policies
18 | mountPath: /etc/kubernetes/policies
19 | readOnly: true
20 | pathType: "DirectoryOrCreate"
21 | - name: "audit-logs"
22 | hostPath: "/var/log/kubernetes"
23 | mountPath: "/var/log/kubernetes"
24 | readOnly: false
25 | pathType: DirectoryOrCreate
26 | # mount the local file on the control plane
27 | extraMounts:
28 | - hostPath: ./audit-policy.yaml
29 | containerPath: /etc/kubernetes/policies/audit-policy.yaml
30 | readOnly: true
31 |
--------------------------------------------------------------------------------
/labs/benchmark/Makefile:
--------------------------------------------------------------------------------
1 | prom:
2 | docker run --network=host -p 9090:9090 -v $(shell pwd)/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus --config.file=/etc/prometheus/prometheus.yml
3 |
--------------------------------------------------------------------------------
/labs/benchmark/dashboards/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grafana/grafana-operator/953272667a20556c0a6d4fcd63f403170a160040/labs/benchmark/dashboards/.gitkeep
--------------------------------------------------------------------------------
/labs/benchmark/datasources/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grafana/grafana-operator/953272667a20556c0a6d4fcd63f403170a160040/labs/benchmark/datasources/.gitkeep
--------------------------------------------------------------------------------
/labs/benchmark/prometheus.yml:
--------------------------------------------------------------------------------
1 | global:
2 | scrape_interval: 10s
3 | evaluation_interval: 30s
4 | scrape_configs:
5 | - job_name: "grafana"
6 | static_configs:
7 | - targets: ["localhost:8080"]
8 |
--------------------------------------------------------------------------------
/media/slack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grafana/grafana-operator/953272667a20556c0a6d4fcd63f403170a160040/media/slack.png
--------------------------------------------------------------------------------
/tests/e2e/conditions/03-additional-invalid-spec.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: GrafanaNotificationPolicy
4 | metadata:
5 | name: additional-testdata-policy
6 | spec:
7 | instanceSelector:
8 | matchLabels:
9 | test: ($test.metadata.name)
10 | resyncPeriod: 3s
11 | route:
12 | receiver: testdata
13 | group_by:
14 | - grafana_folder
15 | - alertname
16 | routes:
17 | - receiver: grafana-default-email
18 | object_matchers:
19 | - - type
20 | - =
21 | - static
22 | routes:
23 | - receiver: grafana-default-email
24 | object_matchers:
25 | - - type
26 | - =
27 | - static_child
28 | routeSelector:
29 | matchLabels:
30 | dynamic: "child"
31 | ---
32 | apiVersion: grafana.integreatly.org/v1beta1
33 | kind: GrafanaDashboard
34 | metadata:
35 | name: no-resolvable-model
36 | spec:
37 | resyncPeriod: 3s
38 | instanceSelector:
39 | matchLabels:
40 | test: "($test.metadata.name)"
41 |
--------------------------------------------------------------------------------
/tests/e2e/conditions/03-testdata-invalid-specs.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # Make folder reference itself as parent
3 | apiVersion: grafana.integreatly.org/v1beta1
4 | kind: GrafanaFolder
5 | metadata:
6 | name: testdata
7 | spec:
8 | parentFolderUID: testdata-uid
9 | ---
10 | # Reference non-existing secret
11 | apiVersion: grafana.integreatly.org/v1beta1
12 | kind: GrafanaContactPoint
13 | metadata:
14 | name: testdata
15 | spec:
16 | # Override settings
17 | settings: {}
18 | valuesFrom:
19 | - targetPath: addresses
20 | valueFrom:
21 | secretKeyRef:
22 | name: contact-mails
23 | key: alert-mails
24 | ---
25 | # Introduce a loop and trigger loop detected
26 | apiVersion: grafana.integreatly.org/v1beta1
27 | kind: GrafanaNotificationPolicyRoute
28 | metadata:
29 | name: team-c
30 | spec:
31 | routeSelector:
32 | matchLabels:
33 | team-b: "child"
34 | ---
35 | # Model is not valid JSON
36 | apiVersion: grafana.integreatly.org/v1beta1
37 | kind: GrafanaDashboard
38 | metadata:
39 | name: testdata
40 | spec:
41 | json: "{"
42 | ---
43 | # Reference non-existing secret
44 | apiVersion: grafana.integreatly.org/v1beta1
45 | kind: GrafanaDatasource
46 | metadata:
47 | name: testdata
48 | spec:
49 | valuesFrom:
50 | - targetPath: secureJsonData.httpHeaderValue1
51 | valueFrom:
52 | secretKeyRef:
53 | name: credentials
54 | key: "PROMETHEUS_TOKEN"
55 |
56 | # TODO Grafana when InvalidSpec is implemented for external Grafana admin secret
57 | # Reference non-existing secret
58 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/00-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: Grafana
3 | metadata:
4 | name: grafana
5 | spec:
6 | version: 11.3.0
7 | status:
8 | (wildcard('http://grafana-service.*:3000', adminUrl || '')): true
9 | stage: complete
10 | stageStatus: success
11 | version: 11.3.0
12 | ---
13 | apiVersion: grafana.integreatly.org/v1beta1
14 | kind: Grafana
15 | metadata:
16 | name: external-grafana
17 | status:
18 | adminUrl: (join('',['http://grafana-internal-service.',$namespace,':3000']))
19 | stage: complete
20 | stageStatus: success
21 | version: 11.3.0
22 | ---
23 | apiVersion: grafana.integreatly.org/v1beta1
24 | kind: Grafana
25 | metadata:
26 | name: grafana-versioned
27 | status:
28 | stage: complete
29 | stageStatus: success
30 | version: 10.3.5
31 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/00-create-external-grafana.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana-internal
6 | labels:
7 | dashboards: "grafana-internal"
8 | spec:
9 | client:
10 | preferIngress: false
11 | config:
12 | log:
13 | mode: "console"
14 | auth:
15 | disable_login_form: "false"
16 | security:
17 | admin_user: root
18 | admin_password: secret
19 | ingress:
20 | spec:
21 | ingressClassName: nginx
22 | rules:
23 | - host: test.io
24 | http:
25 | paths:
26 | - backend:
27 | service:
28 | name: grafana-internal-service
29 | port:
30 | number: 3000
31 | path: /
32 | pathType: Prefix
33 | ---
34 | # Use the same grafana instance that we just created, notice the ingress config
35 | apiVersion: grafana.integreatly.org/v1beta1
36 | kind: Grafana
37 | metadata:
38 | name: external-grafana
39 | labels:
40 | dashboards: "external-grafana"
41 | spec:
42 | external:
43 | url: (join('',['http://grafana-internal-service.',$namespace,':3000']))
44 | adminPassword:
45 | name: grafana-internal-admin-credentials
46 | key: GF_SECURITY_ADMIN_PASSWORD
47 | adminUser:
48 | name: grafana-internal-admin-credentials
49 | key: GF_SECURITY_ADMIN_USER
50 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/00-create-grafana.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: Grafana
3 | metadata:
4 | name: grafana
5 | labels:
6 | dashboards: "grafana"
7 | spec:
8 | client:
9 | preferIngress: false
10 | config:
11 | log:
12 | mode: "console"
13 | auth:
14 | disable_login_form: "false"
15 | security:
16 | admin_user: root
17 | admin_password: secret
18 | ## Required to test creation of Grafana-managed recording rules
19 | feature_toggles:
20 | grafanaManagedRecordingRules: "true"
21 | recording_rules:
22 | enabled: "true"
23 | url: http://prometheus:9090/api/prom/push
24 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/00-create-versioned-grafana.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: Grafana
3 | metadata:
4 | name: grafana-versioned
5 | labels:
6 | dashboards: "grafana"
7 | spec:
8 | client:
9 | preferIngress: false
10 | version: 10.3.5
11 | config:
12 | log:
13 | mode: "console"
14 | auth:
15 | disable_login_form: "false"
16 | security:
17 | admin_user: root
18 | admin_password: secret
19 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/01-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: grafana-deployment
5 | status:
6 | availableReplicas: 1
7 | ---
8 | apiVersion: grafana.integreatly.org/v1beta1
9 | kind: GrafanaDatasource
10 | metadata:
11 | name: grafanadatasource-sample
12 | status:
13 | conditions:
14 | - type: DatasourceSynchronized
15 | status: "True"
16 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/01-datasource.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDatasource
3 | metadata:
4 | name: grafanadatasource-sample
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | dashboards: "grafana"
9 | plugins:
10 | - name: grafana-clock-panel
11 | version: 1.3.0
12 | datasource:
13 | name: prometheus
14 | type: prometheus
15 | access: proxy
16 | url: http://prometheus-service:9090
17 | isDefault: true
18 | jsonData:
19 | "tlsSkipVerify": true
20 | "timeInterval": "5s"
21 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/02-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: grafana-plugins
5 | binaryData:
6 | grafanadatasource-sample-datasource: W3sibmFtZSI6ImdyYWZhbmEtY2xvY2stcGFuZWwiLCJ2ZXJzaW9uIjoiMS4zLjAifV0=
7 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/03-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: grafana-plugins
5 | binaryData:
6 | grafana-dashboard-dashboard: W3sibmFtZSI6ImdyYWZhbmEtcGllY2hhcnQtcGFuZWwiLCJ2ZXJzaW9uIjoiMS4zLjkifV0=
7 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/03-dashboard.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDashboard
3 | metadata:
4 | name: grafana-dashboard
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | dashboards: "grafana"
9 | plugins:
10 | - name: grafana-piechart-panel
11 | version: 1.3.9
12 | json: >
13 | {
14 | "id": null,
15 | "title": "Simple Dashboard",
16 | "tags": [],
17 | "style": "dark",
18 | "timezone": "browser",
19 | "editable": true,
20 | "hideControls": false,
21 | "graphTooltip": 1,
22 | "panels": [],
23 | "time": {
24 | "from": "now-6h",
25 | "to": "now"
26 | },
27 | "timepicker": {
28 | "time_options": [],
29 | "refresh_intervals": []
30 | },
31 | "templating": {
32 | "list": []
33 | },
34 | "annotations": {
35 | "list": []
36 | },
37 | "refresh": "5s",
38 | "schemaVersion": 17,
39 | "version": 0,
40 | "links": []
41 | }
42 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/04-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: grafana-plugins
5 | binaryData:
6 | grafana-dashboard-dashboard: W3sibmFtZSI6ImdyYWZhbmEtcGllY2hhcnQtcGFuZWwiLCJ2ZXJzaW9uIjoiMS4zLjkifV0=
7 | ---
8 | apiVersion: grafana.integreatly.org/v1beta1
9 | kind: GrafanaDashboard
10 | metadata:
11 | name: grafana-dashboard-inline-envs
12 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/04-dashboard.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaDashboard
3 | metadata:
4 | name: grafana-dashboard-inline-envs
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | dashboards: "grafana"
9 | envs:
10 | - name: CUSTOM_RANGE_ENV
11 | value: "now - 12h"
12 | plugins:
13 | - name: grafana-piechart-panel
14 | version: 1.3.9
15 | jsonnet: >
16 | local myRange = std.extVar('CUSTOM_RANGE_ENV');
17 | {
18 | id: null,
19 | title: "Simple Dashboard With Inline Envs",
20 | tags: [],
21 | style: "dark",
22 | timezone: "browser",
23 | editable: true,
24 | hideControls: false,
25 | graphTooltip: 1,
26 | panels: [],
27 | time: {
28 | from: myRange,
29 | to: "now"
30 | },
31 | timepicker: {
32 | time_options: [],
33 | refresh_intervals: []
34 | },
35 | templating: {
36 | list: []
37 | },
38 | annotations: {
39 | list: []
40 | },
41 | refresh: "5s",
42 | schemaVersion: 17,
43 | version: 0,
44 | links: []
45 | }
46 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/05-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: grafana-plugins
5 | binaryData:
6 | grafana-dashboard-dashboard: W3sibmFtZSI6ImdyYWZhbmEtcGllY2hhcnQtcGFuZWwiLCJ2ZXJzaW9uIjoiMS4zLjkifV0=
7 | ---
8 | apiVersion: grafana.integreatly.org/v1beta1
9 | kind: GrafanaDashboard
10 | metadata:
11 | name: grafana-dashboard-cm-envs
12 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/05-dashboard.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: grafana-user-envs
5 | data:
6 | CUSTOM_RANGE_ENV: "now-1h"
7 | ---
8 | apiVersion: grafana.integreatly.org/v1beta1
9 | kind: GrafanaDashboard
10 | metadata:
11 | name: grafana-dashboard-cm-envs
12 | spec:
13 | instanceSelector:
14 | matchLabels:
15 | dashboards: "grafana"
16 | envFrom:
17 | - configMapKeyRef:
18 | name: grafana-user-envs
19 | key: "CUSTOM_RANGE_ENV"
20 | plugins:
21 | - name: grafana-piechart-panel
22 | version: 1.3.9
23 | jsonnet: >
24 | local myRange = std.extVar('CUSTOM_RANGE_ENV');
25 | {
26 | id: null,
27 | title: "Simple Dashboard with CM envs",
28 | tags: [],
29 | style: "dark",
30 | timezone: "browser",
31 | editable: true,
32 | hideControls: false,
33 | graphTooltip: 1,
34 | panels: [],
35 | time: {
36 | from: myRange,
37 | to: "now"
38 | },
39 | timepicker: {
40 | time_options: [],
41 | refresh_intervals: []
42 | },
43 | templating: {
44 | list: []
45 | },
46 | annotations: {
47 | list: []
48 | },
49 | refresh: "5s",
50 | schemaVersion: 17,
51 | version: 0,
52 | links: []
53 | }
54 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/06-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: grafana-plugins
5 | binaryData:
6 | grafana-dashboard-dashboard: W3sibmFtZSI6ImdyYWZhbmEtcGllY2hhcnQtcGFuZWwiLCJ2ZXJzaW9uIjoiMS4zLjkifV0=
7 | ---
8 | apiVersion: grafana.integreatly.org/v1beta1
9 | kind: GrafanaDashboard
10 | metadata:
11 | name: grafana-dashboard-secret-envs
12 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/06-dashboard.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: grafana-user-secrets
5 | stringData:
6 | CUSTOM_RANGE_ENV: "now-2h"
7 | ---
8 | apiVersion: grafana.integreatly.org/v1beta1
9 | kind: GrafanaDashboard
10 | metadata:
11 | name: grafana-dashboard-secret-envs
12 | spec:
13 | instanceSelector:
14 | matchLabels:
15 | dashboards: "grafana"
16 | envFrom:
17 | - secretKeyRef:
18 | name: grafana-user-secrets
19 | key: "CUSTOM_RANGE_ENV"
20 | plugins:
21 | - name: grafana-piechart-panel
22 | version: 1.3.9
23 | jsonnet: >
24 | local myRange = std.extVar('CUSTOM_RANGE_ENV');
25 | {
26 | id: null,
27 | title: "Simple Dashboard with Envs from secret",
28 | tags: [],
29 | style: "dark",
30 | timezone: "browser",
31 | editable: true,
32 | hideControls: false,
33 | graphTooltip: 1,
34 | panels: [],
35 | time: {
36 | from: myRange,
37 | to: "now"
38 | },
39 | timepicker: {
40 | time_options: [],
41 | refresh_intervals: []
42 | },
43 | templating: {
44 | list: []
45 | },
46 | annotations: {
47 | list: []
48 | },
49 | refresh: "5s",
50 | schemaVersion: 17,
51 | version: 0,
52 | links: []
53 | }
54 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/07-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: GrafanaDashboard
4 | metadata:
5 | name: grafana-dashboard-jsonnet-project
6 | ---
7 | apiVersion: grafana.integreatly.org/v1beta1
8 | kind: GrafanaDashboard
9 | metadata:
10 | name: jsonnet-env-vars
11 | status:
12 | conditions:
13 | - type: DashboardSynchronized
14 | status: "True"
15 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/08-alert-folder.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaFolder
3 | metadata:
4 | name: test-folder-from-operator
5 | labels:
6 | folder: "test-folder"
7 | spec:
8 | resyncPeriod: 30s
9 | instanceSelector:
10 | matchLabels:
11 | dashboards: "grafana"
12 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/08-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaAlertRuleGroup
3 | metadata:
4 | name: test
5 | status:
6 | conditions:
7 | - type: AlertGroupSynchronized
8 | status: "True"
9 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/08-folder-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaFolder
3 | metadata:
4 | name: test-folder-from-operator
5 | status:
6 | conditions:
7 | - reason: ApplySuccessful
8 | status: "True"
9 | type: FolderSynchronized
10 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/09-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaContactPoint
3 | metadata:
4 | name: test
5 | status:
6 | conditions:
7 | - type: ContactPointSynchronized
8 | status: "True"
9 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/09-contactpoint.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaContactPoint
3 | metadata:
4 | name: test
5 | spec:
6 | name: test
7 | type: "email"
8 | instanceSelector:
9 | matchLabels:
10 | dashboards: "grafana"
11 | settings:
12 | addresses: "email@email.com"
13 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/10-assert-contact-points.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaContactPoint
3 | metadata:
4 | name: first-test
5 | status:
6 | conditions:
7 | - reason: ApplySuccessful
8 | status: "True"
9 | type: ContactPointSynchronized
10 | ---
11 | apiVersion: grafana.integreatly.org/v1beta1
12 | kind: GrafanaContactPoint
13 | metadata:
14 | name: second-test
15 | status:
16 | conditions:
17 | - reason: ApplySuccessful
18 | status: "True"
19 | type: ContactPointSynchronized
20 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/10-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaNotificationPolicy
3 | metadata:
4 | name: test
5 | status:
6 | conditions:
7 | - type: NotificationPolicySynchronized
8 | status: "True"
9 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/10-contact-points.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaContactPoint
3 | metadata:
4 | name: first-test
5 | spec:
6 | name: first-test
7 | type: "email"
8 | resyncPeriod: 30s
9 | instanceSelector:
10 | matchLabels:
11 | dashboards: "grafana"
12 | settings:
13 | addresses: "email@email.com"
14 | ---
15 | apiVersion: grafana.integreatly.org/v1beta1
16 | kind: GrafanaContactPoint
17 | metadata:
18 | name: second-test
19 | spec:
20 | name: second-test
21 | type: "email"
22 | resyncPeriod: 30s
23 | instanceSelector:
24 | matchLabels:
25 | dashboards: "grafana"
26 | settings:
27 | addresses: "email@email.com"
28 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/10-notification-policy.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: GrafanaNotificationPolicy
4 | metadata:
5 | name: test
6 | spec:
7 | instanceSelector:
8 | matchLabels:
9 | dashboards: "grafana"
10 | route:
11 | receiver: grafana-default-email
12 | group_by:
13 | - grafana_folder
14 | - alertname
15 | routes:
16 | - receiver: first-test
17 | object_matchers:
18 | - - foo
19 | - =
20 | - bar
21 | routes:
22 | - receiver: second-test
23 | object_matchers:
24 | - - severity
25 | - =
26 | - critical
27 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/11-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: Grafana
3 | metadata:
4 | name: grafana-tls
5 | spec:
6 | version: 11.3.0
7 | status:
8 | (wildcard('https://grafana-tls-service.*:3000', adminUrl || '')): true
9 | stage: complete
10 | stageStatus: success
11 | version: 11.3.0
12 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/12-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaNotificationTemplate
3 | metadata:
4 | name: test
5 | status:
6 | conditions:
7 | - type: NotificationTemplateSynchronized
8 | status: "True"
9 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/12-notification-template.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: GrafanaNotificationTemplate
4 | metadata:
5 | name: test
6 | spec:
7 | instanceSelector:
8 | matchLabels:
9 | dashboards: "grafana"
10 | name: test
11 | template: |
12 | {{ define "SlackAlert" }}
13 | [{{.Status}}] {{ .Labels.alertname }}
14 | {{ .Annotations.AlertValues }}
15 | {{ end }}
16 |
17 | {{ define "SlackAlertMessage" }}
18 | {{ if gt (len .Alerts.Firing) 0 }}
19 | {{ len .Alerts.Firing }} firing:
20 | {{ range .Alerts.Firing }} {{ template "SlackAlert" . }} {{ end }}
21 | {{ end }}
22 | {{ if gt (len .Alerts.Resolved) 0 }}
23 | {{ len .Alerts.Resolved }} resolved:
24 | {{ range .Alerts.Resolved }} {{ template "SlackAlert" . }} {{ end }}
25 | {{ end }}
26 | {{ end }}
27 |
28 | {{ template "SlackAlertMessage" . }}
29 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/13-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaMuteTiming
3 | metadata:
4 | name: mutetiming-sample
5 | status:
6 | conditions:
7 | - type: MuteTimingSynchronized
8 | status: "True"
9 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/13-mute-timing.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: GrafanaMuteTiming
4 | metadata:
5 | name: mutetiming-sample
6 | spec:
7 | instanceSelector:
8 | matchLabels:
9 | dashboards: "grafana"
10 | name: mutetiming-sample
11 | editable: false
12 | time_intervals:
13 | - times:
14 | - start_time: "00:00"
15 | end_time: "06:00"
16 | weekdays: [saturday]
17 | days_of_month: ["1", "15"]
18 | location: Asia/Shanghai
19 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/14-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: grafana-plugins
5 | binaryData:
6 | grafana-library-panel-librarypanel: W3sibmFtZSI6ImdyYWZhbmEtcGllY2hhcnQtcGFuZWwiLCJ2ZXJzaW9uIjoiMS4zLjkifV0=
7 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/14-library-panel.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaLibraryPanel
3 | metadata:
4 | name: grafana-library-panel
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | dashboards: "grafana"
9 | plugins:
10 | - name: grafana-piechart-panel
11 | version: 1.3.9
12 | json: >
13 | {
14 | "uid": "V--OrYHnz",
15 | "name": "API docs Example",
16 | "kind": 1,
17 | "type": "text",
18 | "description": "",
19 | "model": {},
20 | "version": 1
21 | }
22 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/15-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: grafana-plugins
5 | binaryData:
6 | grafana-library-panel-inline-envs-librarypanel: W3sibmFtZSI6ImdyYWZhbmEtcGllY2hhcnQtcGFuZWwiLCJ2ZXJzaW9uIjoiMS4zLjkifV0=
7 | ---
8 | apiVersion: grafana.integreatly.org/v1beta1
9 | kind: GrafanaLibraryPanel
10 | metadata:
11 | name: grafana-library-panel-inline-envs
12 |
--------------------------------------------------------------------------------
/tests/e2e/example-test/15-library-panel.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: GrafanaLibraryPanel
3 | metadata:
4 | name: grafana-library-panel-inline-envs
5 | spec:
6 | instanceSelector:
7 | matchLabels:
8 | dashboards: "grafana"
9 | envs:
10 | - name: CUSTOM_RANGE_ENV
11 | value: "now - 12h"
12 | plugins:
13 | - name: grafana-piechart-panel
14 | version: 1.3.9
15 | jsonnet: >
16 | local myRange = std.extVar('CUSTOM_RANGE_ENV');
17 | {
18 | model: {}
19 | }
20 |
--------------------------------------------------------------------------------
/tests/e2e/examples/basic/assertions.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: grafana-deployment
5 | ownerReferences:
6 | - apiVersion: grafana.integreatly.org/v1beta1
7 | kind: Grafana
8 | name: grafana
9 | spec: {}
10 | ---
11 | apiVersion: v1
12 | kind: Service
13 | metadata:
14 | name: grafana-service
15 | ownerReferences:
16 | - apiVersion: grafana.integreatly.org/v1beta1
17 | kind: Grafana
18 | name: grafana
19 | spec: {}
20 | ---
21 | apiVersion: v1
22 | kind: Service
23 | metadata:
24 | name: grafana-alerting
25 | ownerReferences:
26 | - apiVersion: grafana.integreatly.org/v1beta1
27 | kind: Grafana
28 | name: grafana
29 | spec: {}
30 | ---
31 | apiVersion: v1
32 | kind: ConfigMap
33 | metadata:
34 | name: grafana-ini
35 | ownerReferences:
36 | - apiVersion: grafana.integreatly.org/v1beta1
37 | kind: Grafana
38 | name: grafana
39 | ---
40 | apiVersion: v1
41 | kind: ConfigMap
42 | metadata:
43 | name: grafana-plugins
44 | ownerReferences:
45 | - apiVersion: grafana.integreatly.org/v1beta1
46 | kind: Grafana
47 | name: grafana
48 |
--------------------------------------------------------------------------------
/tests/e2e/examples/basic/chainsaw-test.yaml:
--------------------------------------------------------------------------------
1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json
2 | apiVersion: chainsaw.kyverno.io/v1alpha1
3 | kind: Test
4 | metadata:
5 | name: basic
6 | spec:
7 | steps:
8 | - name: step-00
9 | try:
10 | - apply:
11 | file: resources.yaml
12 | - assert:
13 | file: assertions.yaml
14 |
--------------------------------------------------------------------------------
/tests/e2e/examples/basic/resources.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: grafana.integreatly.org/v1beta1
2 | kind: Grafana
3 | metadata:
4 | name: grafana
5 | labels:
6 | dashboards: "grafana"
7 | spec:
8 | config:
9 | log:
10 | mode: "console"
11 | auth:
12 | disable_login_form: "false"
13 | security:
14 | admin_user: root
15 | admin_password: secret
16 | ---
17 | apiVersion: grafana.integreatly.org/v1beta1
18 | kind: GrafanaDashboard
19 | metadata:
20 | name: grafanadashboard-sample
21 | spec:
22 | resyncPeriod: 30s
23 | instanceSelector:
24 | matchLabels:
25 | dashboards: "grafana"
26 | json: >
27 | {
28 | "id": null,
29 | "title": "Simple Dashboard",
30 | "tags": [],
31 | "style": "dark",
32 | "timezone": "browser",
33 | "editable": true,
34 | "hideControls": false,
35 | "graphTooltip": 1,
36 | "panels": [],
37 | "time": {
38 | "from": "now-6h",
39 | "to": "now"
40 | },
41 | "timepicker": {
42 | "time_options": [],
43 | "refresh_intervals": []
44 | },
45 | "templating": {
46 | "list": []
47 | },
48 | "annotations": {
49 | "list": []
50 | },
51 | "refresh": "5s",
52 | "schemaVersion": 17,
53 | "version": 0,
54 | "links": []
55 | }
56 |
--------------------------------------------------------------------------------
/tests/e2e/examples/crossnamespace/chainsaw-test.yaml:
--------------------------------------------------------------------------------
1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json
2 | apiVersion: chainsaw.kyverno.io/v1alpha1
3 | kind: Test
4 | metadata:
5 | name: crossnamespace
6 | spec:
7 | concurrent: false
8 | steps:
9 | - name: step-00
10 | try:
11 | - apply:
12 | template: true
13 | file: resources.yaml
14 | - assert:
15 | template: true
16 | file: assertions.yaml
17 |
--------------------------------------------------------------------------------
/tests/e2e/examples/dashboard_immutable_uid/base-resources.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: grafana.integreatly.org/v1beta1
3 | kind: Grafana
4 | metadata:
5 | name: grafana-immutable
6 | labels:
7 | immutable: "grafana"
8 | spec:
9 | config:
10 | log:
11 | mode: "console"
12 | auth:
13 | disable_login_form: "false"
14 | security:
15 | admin_user: root
16 | admin_password: secret
17 | ---
18 | apiVersion: grafana.integreatly.org/v1beta1
19 | kind: GrafanaDashboard
20 | metadata:
21 | name: dashboard-uid
22 | spec:
23 | instanceSelector:
24 | matchLabels:
25 | immutable: "grafana"
26 | resyncPeriod: 3s
27 | json: ($dashboardModel)
28 | ---
29 | apiVersion: grafana.integreatly.org/v1beta1
30 | kind: GrafanaDashboard
31 | metadata:
32 | name: metadata-uid
33 | spec:
34 | instanceSelector:
35 | matchLabels:
36 | immutable: "grafana"
37 | resyncPeriod: 3s
38 | json: ($dashboardModel)
39 | ---
40 | apiVersion: grafana.integreatly.org/v1beta1
41 | kind: GrafanaDashboard
42 | metadata:
43 | name: spec-uid
44 | spec:
45 | instanceSelector:
46 | matchLabels:
47 | immutable: "grafana"
48 | resyncPeriod: 3s
49 | json: ($dashboardModel)
50 | uid: SpecUID
51 |
--------------------------------------------------------------------------------