Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: regenerate grafana deployment when credentials get changed #786

Merged
merged 3 commits into from Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions controllers/constants/constants.go
Expand Up @@ -22,7 +22,9 @@ const (
GrafanaHealthEndpoint = "/api/health"
GrafanaPodLabel = "grafana"
LastConfigAnnotation = "last-config"
LastCredentialsAnnotation = "last-credentials"
LastConfigEnvVar = "LAST_CONFIG"
LastCredentialsEnvVar = "LAST_CREDENTIALS"
LastDatasourcesConfigEnvVar = "LAST_DATASOURCES"
GrafanaAdminSecretName = "grafana-admin-credentials" // #nosec G101
DefaultAdminUser = "admin"
Expand Down
24 changes: 16 additions & 8 deletions controllers/grafana/grafana_reconciler.go
Expand Up @@ -14,10 +14,11 @@ import (
)

type GrafanaReconciler struct {
DsHash string
ConfigHash string
PluginsEnv string
Plugins *PluginsHelperImpl
DsHash string
ConfigHash string
CredentialsHash string
PluginsEnv string
Plugins *PluginsHelperImpl
}

func NewGrafanaReconciler() *GrafanaReconciler {
Expand Down Expand Up @@ -225,13 +226,20 @@ func (i *GrafanaReconciler) getGrafanaAdminUserSecretDesiredState(state *common.
}

if state.AdminSecret == nil {
secret := model.AdminSecret(cr)
i.CredentialsHash = secret.Annotations[constants.LastCredentialsAnnotation]

return common.GenericCreateAction{
Ref: model.AdminSecret(cr),
Ref: secret,
Msg: "create admin credentials secret",
}
}

secret := model.AdminSecretReconciled(cr, state.AdminSecret)
i.CredentialsHash = secret.Annotations[constants.LastCredentialsAnnotation]

return common.GenericUpdateAction{
Ref: model.AdminSecretReconciled(cr, state.AdminSecret),
Ref: secret,
Msg: "update admin credentials secret",
}
}
Expand Down Expand Up @@ -265,14 +273,14 @@ func (i *GrafanaReconciler) getGrafanaRouteDesiredState(state *common.ClusterSta
func (i *GrafanaReconciler) getGrafanaDeploymentDesiredState(state *common.ClusterState, cr *v1alpha1.Grafana) common.ClusterAction {
if state.GrafanaDeployment == nil {
return common.GenericCreateAction{
Ref: model.GrafanaDeployment(cr, i.ConfigHash, i.DsHash),
Ref: model.GrafanaDeployment(cr, i.ConfigHash, i.DsHash, i.CredentialsHash),
Msg: "create grafana deployment",
}
}

return common.GenericUpdateAction{
Ref: model.GrafanaDeploymentReconciled(cr, state.GrafanaDeployment,
i.ConfigHash, i.PluginsEnv, i.DsHash),
i.ConfigHash, i.PluginsEnv, i.DsHash, i.CredentialsHash),
Msg: "update grafana deployment",
}
}
Expand Down
34 changes: 28 additions & 6 deletions controllers/model/adminUserSecret.go
@@ -1,6 +1,9 @@
package model

import (
"bytes"
"crypto/sha256"
"fmt"
"os"

"github.com/grafana-operator/grafana-operator/v4/api/integreatly/v1alpha1"
Expand Down Expand Up @@ -32,34 +35,53 @@ func getAdminPassword(cr *v1alpha1.Grafana, current *v12.Secret) []byte {
return []byte(cr.Spec.Config.Security.AdminPassword)
}

func getData(cr *v1alpha1.Grafana, current *v12.Secret) map[string][]byte {
func getData(cr *v1alpha1.Grafana, current *v12.Secret) (map[string][]byte, string) {
user := getAdminUser(cr, current)
password := getAdminPassword(cr, current)

credentials := map[string][]byte{
constants.GrafanaAdminUserEnvVar: getAdminUser(cr, current),
constants.GrafanaAdminPasswordEnvVar: getAdminPassword(cr, current),
constants.GrafanaAdminUserEnvVar: user,
constants.GrafanaAdminPasswordEnvVar: password,
}

h := sha256.New()
h.Write(bytes.Join([][]byte{user, password}, []byte(":")))
hash := fmt.Sprintf("%x", h.Sum(nil))

// Make the credentials available to the environment when running the operator
// outside of the cluster
os.Setenv(constants.GrafanaAdminUserEnvVar, string(credentials[constants.GrafanaAdminUserEnvVar]))
os.Setenv(constants.GrafanaAdminPasswordEnvVar, string(credentials[constants.GrafanaAdminPasswordEnvVar]))

return credentials
return credentials, hash
}

func AdminSecret(cr *v1alpha1.Grafana) *v12.Secret {
data, hash := getData(cr, nil)

return &v12.Secret{
ObjectMeta: v1.ObjectMeta{
Name: constants.GrafanaAdminSecretName,
Namespace: cr.Namespace,
Annotations: map[string]string{
constants.LastCredentialsAnnotation: hash,
},
},
Data: getData(cr, nil),
Data: data,
Type: v12.SecretTypeOpaque,
}
}

func AdminSecretReconciled(cr *v1alpha1.Grafana, currentState *v12.Secret) *v12.Secret {
data, hash := getData(cr, currentState)

reconciled := currentState.DeepCopy()
reconciled.Data = getData(cr, currentState)
reconciled.Data = data

reconciled.Annotations = map[string]string{
constants.LastCredentialsAnnotation: hash,
}

return reconciled
}

Expand Down
46 changes: 26 additions & 20 deletions controllers/model/grafanaDeployment.go
Expand Up @@ -83,23 +83,23 @@ func getResources(cr *v1alpha1.Grafana) v13.ResourceRequirements {
}

func getAffinities(cr *v1alpha1.Grafana) *v13.Affinity {
var affinity = v13.Affinity{}
affinity := v13.Affinity{}
if cr.Spec.Deployment != nil && cr.Spec.Deployment.Affinity != nil {
affinity = *cr.Spec.Deployment.Affinity
}
return &affinity
}

func getSecurityContext(cr *v1alpha1.Grafana) *v13.PodSecurityContext {
var securityContext = v13.PodSecurityContext{}
securityContext := v13.PodSecurityContext{}
if cr.Spec.Deployment != nil && cr.Spec.Deployment.SecurityContext != nil {
securityContext = *cr.Spec.Deployment.SecurityContext
}
return &securityContext
}

func getContainerSecurityContext(cr *v1alpha1.Grafana) *v13.SecurityContext {
var containerSecurityContext = v13.SecurityContext{}
containerSecurityContext := v13.SecurityContext{}
if cr.Spec.Deployment != nil && cr.Spec.Deployment.ContainerSecurityContext != nil {
containerSecurityContext = *cr.Spec.Deployment.ContainerSecurityContext
}
Expand All @@ -118,15 +118,15 @@ func getDeploymentStrategy(cr *v1alpha1.Grafana) v1.DeploymentStrategy {
}

func getDeploymentLabels(cr *v1alpha1.Grafana) map[string]string {
var labels = map[string]string{}
labels := map[string]string{}
if cr.Spec.Deployment != nil && cr.Spec.Deployment.Labels != nil {
labels = cr.Spec.Deployment.Labels
}
return labels
}

func getDeploymentAnnotations(cr *v1alpha1.Grafana, existing map[string]string) map[string]string {
var annotations = map[string]string{}
annotations := map[string]string{}
// Add fixed annotations
annotations["prometheus.io/scrape"] = "true"
annotations["prometheus.io/port"] = fmt.Sprintf("%v", GetGrafanaPort(cr))
Expand All @@ -148,7 +148,7 @@ func getRollingUpdateStrategy() *v1.RollingUpdateDeployment {
}

func getPodAnnotations(cr *v1alpha1.Grafana, existing map[string]string) map[string]string {
var annotations = map[string]string{}
annotations := map[string]string{}
// Add fixed annotations
annotations["prometheus.io/scrape"] = "true"
annotations["prometheus.io/port"] = fmt.Sprintf("%v", GetGrafanaPort(cr))
Expand All @@ -161,7 +161,7 @@ func getPodAnnotations(cr *v1alpha1.Grafana, existing map[string]string) map[str
}

func getPodLabels(cr *v1alpha1.Grafana) map[string]string {
var labels = map[string]string{}
labels := map[string]string{}
if cr.Spec.Deployment != nil && cr.Spec.Deployment.Labels != nil {
labels = cr.Spec.Deployment.Labels
}
Expand All @@ -170,7 +170,7 @@ func getPodLabels(cr *v1alpha1.Grafana) map[string]string {
}

func getNodeSelectors(cr *v1alpha1.Grafana) map[string]string {
var nodeSelector = map[string]string{}
nodeSelector := map[string]string{}

if cr.Spec.Deployment != nil && cr.Spec.Deployment.NodeSelector != nil {
nodeSelector = cr.Spec.Deployment.NodeSelector
Expand All @@ -196,7 +196,7 @@ func getTolerations(cr *v1alpha1.Grafana) []v13.Toleration {

func getVolumes(cr *v1alpha1.Grafana) []v13.Volume { // nolint
var volumes []v13.Volume // nolint
var volumeOptional = true
volumeOptional := true

volumes = append(volumes, v13.Volume{
Name: constants.GrafanaProvisionPluginVolumeName,
Expand Down Expand Up @@ -451,7 +451,7 @@ func getVolumeMounts(cr *v1alpha1.Grafana) []v13.VolumeMount {
func getLivenessProbe(cr *v1alpha1.Grafana, delay, timeout, failure int32) *v13.Probe {
var period int32 = 10
var success int32 = 1
var scheme = v13.URISchemeHTTP
scheme := v13.URISchemeHTTP
if cr.Spec.Config.Server != nil && cr.Spec.Config.Server.Protocol == "https" {
scheme = v13.URISchemeHTTPS
}
Expand Down Expand Up @@ -499,7 +499,7 @@ func getLivenessProbe(cr *v1alpha1.Grafana, delay, timeout, failure int32) *v13.
func getReadinessProbe(cr *v1alpha1.Grafana, delay, timeout, failure int32) *v13.Probe {
var period int32 = 10
var success int32 = 1
var scheme = v13.URISchemeHTTP
scheme := v13.URISchemeHTTP
if cr.Spec.Config.Server != nil && cr.Spec.Config.Server.Protocol == "https" {
scheme = v13.URISchemeHTTPS
}
Expand Down Expand Up @@ -544,7 +544,7 @@ func getReadinessProbe(cr *v1alpha1.Grafana, delay, timeout, failure int32) *v13
}
}

func getContainers(cr *v1alpha1.Grafana, configHash, dsHash string) []v13.Container { // nolint
func getContainers(cr *v1alpha1.Grafana, configHash, dsHash, credentialsHash string) []v13.Container { // nolint
var containers []v13.Container // nolint
var image string

Expand All @@ -566,6 +566,10 @@ func getContainers(cr *v1alpha1.Grafana, configHash, dsHash string) []v13.Contai
Name: constants.LastDatasourcesConfigEnvVar,
Value: dsHash,
},
{
Name: constants.LastCredentialsEnvVar,
Value: credentialsHash,
},
}
if cr.Spec.Deployment != nil && cr.Spec.Deployment.HttpProxy != nil && cr.Spec.Deployment.HttpProxy.Enabled {
envVars = append(envVars, v13.EnvVar{
Expand Down Expand Up @@ -637,7 +641,8 @@ func getContainers(cr *v1alpha1.Grafana, configHash, dsHash string) []v13.Contai
},
Key: constants.GrafanaAdminPasswordEnvVar,
},
}})
},
})
}
}

Expand Down Expand Up @@ -682,7 +687,7 @@ func getInitContainers(cr *v1alpha1.Grafana, plugins string) []v13.Container {
}
}

var volumeName = constants.GrafanaPluginsVolumeName
volumeName := constants.GrafanaPluginsVolumeName

if cr.Spec.Deployment != nil {
for _, item := range cr.Spec.Deployment.ExtraVolumeMounts {
Expand Down Expand Up @@ -712,7 +717,7 @@ func getInitContainers(cr *v1alpha1.Grafana, plugins string) []v13.Container {
}
}

func getDeploymentSpec(cr *v1alpha1.Grafana, annotations map[string]string, configHash, plugins, dsHash string) v1.DeploymentSpec {
func getDeploymentSpec(cr *v1alpha1.Grafana, annotations map[string]string, configHash, plugins, dsHash, credentialsHash string) v1.DeploymentSpec {
return v1.DeploymentSpec{
Replicas: getReplicas(cr),
Selector: &v12.LabelSelector{
Expand All @@ -733,7 +738,7 @@ func getDeploymentSpec(cr *v1alpha1.Grafana, annotations map[string]string, conf
SecurityContext: getSecurityContext(cr),
Volumes: getVolumes(cr),
InitContainers: getInitContainers(cr, plugins),
Containers: getContainers(cr, configHash, dsHash),
Containers: getContainers(cr, configHash, dsHash, credentialsHash),
ServiceAccountName: constants.GrafanaServiceAccountName,
TerminationGracePeriodSeconds: getTerminationGracePeriod(cr),
PriorityClassName: getPodPriorityClassName(cr),
Expand All @@ -743,15 +748,15 @@ func getDeploymentSpec(cr *v1alpha1.Grafana, annotations map[string]string, conf
}
}

func GrafanaDeployment(cr *v1alpha1.Grafana, configHash, dsHash string) *v1.Deployment {
func GrafanaDeployment(cr *v1alpha1.Grafana, configHash, dsHash, credentialsHash string) *v1.Deployment {
return &v1.Deployment{
ObjectMeta: v12.ObjectMeta{
Name: constants.GrafanaDeploymentName,
Namespace: cr.Namespace,
Labels: getDeploymentLabels(cr),
Annotations: getDeploymentAnnotations(cr, nil),
},
Spec: getDeploymentSpec(cr, nil, configHash, "", dsHash),
Spec: getDeploymentSpec(cr, nil, configHash, "", dsHash, credentialsHash),
}
}

Expand All @@ -762,12 +767,13 @@ func GrafanaDeploymentSelector(cr *v1alpha1.Grafana) client.ObjectKey {
}
}

func GrafanaDeploymentReconciled(cr *v1alpha1.Grafana, currentState *v1.Deployment, configHash, plugins, dshash string) *v1.Deployment {
func GrafanaDeploymentReconciled(cr *v1alpha1.Grafana, currentState *v1.Deployment, configHash, plugins, dshash, credentialsHash string) *v1.Deployment {
reconciled := currentState.DeepCopy()
reconciled.Spec = getDeploymentSpec(cr,
currentState.Spec.Template.Annotations,
configHash,
plugins,
dshash)
dshash,
credentialsHash)
return reconciled
}
4 changes: 2 additions & 2 deletions controllers/model/grafanaDeployment_test.go
Expand Up @@ -22,7 +22,7 @@ func TestGrafanaDeployment_httpProxy(t *testing.T) {
},
},
}
deployment := GrafanaDeployment(cr, "", "")
deployment := GrafanaDeployment(cr, "", "", "")
for _, container := range deployment.Spec.Template.Spec.Containers {
if container.Name != "grafana" {
continue
Expand Down Expand Up @@ -51,7 +51,7 @@ func TestGrafanaDeployment_httpProxy(t *testing.T) {
},
},
}
deployment := GrafanaDeployment(cr, "", "")
deployment := GrafanaDeployment(cr, "", "", "")
for _, container := range deployment.Spec.Template.Spec.Containers {
if container.Name != "grafana" {
continue
Expand Down