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

Display values diff on upgrade #40

Merged
merged 1 commit into from
Apr 19, 2020
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ This will produce the following effect:
| `googlechat.webhookUrl` | `KW_GOOGLECHAT_WEBHOOK_URL` | | The Google Hangouts Chat URL to use. Must be provided by user. |
| `namespaceToWatch` | `KW_NAMESPACE` | `""` | The cluster namespace to watch for Helm operations in. Leave blank to watch all namespaces. |
| `messagePrefix` | `KW_MESSAGE_PREFIX` | | A prefix for every notification sent. Often used to identify the cluster (production, staging etc). |
| `chartValuesDiff.enabled` | `KW_CHART_VALUES_DIFF_ENABLED` | `false` | When `true`, KubeWise will log a diff of the chart values when a package is upgraded or rolled back. This is useful for visualizing changes between package versions. Be extremely careful with this feature as it can leak sensitive chart values. |
| `image.repository` | | `us.gcr.io/larder-prod/kubewise` | Image repository |
| `image.tag` | | `<VERSION>` | Image tag |
| `replicaCount` | | `1` | Number of KubeWise pods to deploy. More than 1 is not desirable |
Expand Down
12 changes: 7 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ module github.com/larderdev/kubewise
go 1.14

require (
github.com/Azure/go-autorest v14.0.0+incompatible
github.com/jmoiron/sqlx v1.2.0 // indirect
github.com/lib/pq v1.3.0 // indirect
github.com/Azure/go-autorest v14.0.0+incompatible
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
github.com/olekukonko/tablewriter v0.0.2
github.com/rubenv/sql-migrate v0.0.0-20200402132117-435005d389bc // indirect
github.com/pmezard/go-difflib v1.0.0
github.com/slack-go/slack v0.6.3
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef // indirect
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
google.golang.org/appengine v1.6.5 // indirect
helm.sh/helm/v3 v3.1.1
k8s.io/api v0.17.3
k8s.io/apimachinery v0.17.3
k8s.io/client-go v0.17.2
k8s.io/helm v2.16.5+incompatible
)
220 changes: 12 additions & 208 deletions go.sum

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion helm_chart/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ sources:
maintainers:
- name: davidtuite
email: [email protected]
url: https://www.davidtuite.com
url: https://www.larder.dev/kubewise

icon: https://github.com/larderdev/kubewise/blob/master/assets/kubewise-mark-blue-512x512.png
2 changes: 2 additions & 0 deletions helm_chart/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,5 @@ spec:
value: "{{ .Values.webhook.method }}"
- name: KW_WEBHOOK_URL
value: "{{ .Values.webhook.url }}"
- name: KW_CHART_VALUES_DIFF_ENABLED
value: "{{ .Values.chartValuesDiff.enabled }}"
3 changes: 3 additions & 0 deletions helm_chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ webhook:
namespaceToWatch: ""
messagePrefix:

chartValuesDiff:
enabled: false

serviceAccount:
# Specifies whether a service account should be created
create: true
Expand Down
47 changes: 47 additions & 0 deletions kwrelease/event.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package kwrelease

import (
"fmt"
"log"
"strconv"
"strings"

"github.com/pmezard/go-difflib/difflib"
"helm.sh/helm/v3/pkg/chartutil"
rspb "helm.sh/helm/v3/pkg/release"
api_v1 "k8s.io/api/core/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -38,6 +41,8 @@ func (e *Event) Init() error {
e.currentRelease = e.GetRelease(e.CurrentReleaseSecret.Name)
e.previousRelease = e.getPreviousRelease()

e.GetConfigDiffYAML()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why call this if you don't capture the return value

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. That's a mistake. I was just forcing it to run during development so the diff would be logged. I removed the log line but forgot to remove this.

I wasn't expecting anyone to review it so I just merged it to master but I will remove this now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to tackle the issue with sensitive values being leaked in a separate PR.


return nil
}

Expand Down Expand Up @@ -137,6 +142,48 @@ func (e *Event) IsAppVersionChanged() bool {
return e.GetAppVersion() != e.GetPreviousAppVersion()
}

// GetConfigDiffYAML is useful to show what has changed during a chart upgrade or rollback. It
// will show a diff of the values file in the Slack message or other notification. Be careful
// with secrets.
func (e *Event) GetConfigDiffYAML() string {
currentReleaseConfigYAML, err := chartutil.Values(e.currentRelease.Config).YAML()

if err != nil {
// Do NOT log the values. Could leak sensitive data.
log.Println("Error rendering current release config to YAML for application:", e.GetAppName())
return ""
}

if e.previousRelease == nil {
return fmt.Sprintf("%s\n", currentReleaseConfigYAML)
}

previousReleaseConfigYAML, err := chartutil.Values(e.previousRelease.Config).YAML()

if err != nil {
// Do NOT log the values. Could leak sensitive data.
log.Println("Error rendering previous release config to YAML for application:", e.GetAppName())
return ""
}

diff := difflib.UnifiedDiff{
A: difflib.SplitLines(previousReleaseConfigYAML),
B: difflib.SplitLines(currentReleaseConfigYAML),
FromFile: "Old Values",
ToFile: "New Values",
Context: 3,
}

diffText, err := difflib.GetUnifiedDiffString(diff)

if err != nil {
log.Println("Error diffing chart for application:", e.GetAppName())
return ""
}

return diffText
}

// GetAction returns the action which is being performed in this Event. It may be an install,
// upgrade or other Event.
func (e *Event) GetAction() Action {
Expand Down
33 changes: 27 additions & 6 deletions presenters/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package presenters

import (
"fmt"
"log"
"os"
"strconv"
"strings"
Expand All @@ -26,6 +27,21 @@ func getChangeInAppVersion(releaseEvent *kwrelease.Event) string {
return appVersion
}

func getConfigDiff(releaseEvent *kwrelease.Event) string {
showDiff, err := strconv.ParseBool(os.Getenv("KW_CHART_VALUES_DIFF_ENABLED"))

if err != nil {
log.Println("Invalid value passed for environment variable KW_CHART_VALUES_DIFF_ENABLED. Boolean required.")
}

configDiffYAML := releaseEvent.GetConfigDiffYAML()
if showDiff && configDiffYAML != "" {
return fmt.Sprintf("\n```%s```", configDiffYAML)
}

return ""
}

// PrepareMsg prepares a short, markdown-like message which is suitable for sending to chat
// applications like Slack. Formatting like *text* us used to add emphasis. This is supported by
// both Slack and Google Chat. Emoji are also used liberally.
Expand All @@ -51,6 +67,10 @@ func PrepareMsg(releaseEvent *kwrelease.Event) string {
getChangeInAppVersion(releaseEvent),
)

if configDiff := getConfigDiff(releaseEvent); configDiff != "" {
msg += configDiff
}

case kwrelease.ActionPreRollback:
msg += fmt.Sprintf("⏬ Rolling back *%s* from version %s to version *%s* in namespace *%s* via Helm. ⏳\n%s",
releaseEvent.GetAppName(),
Expand All @@ -60,6 +80,10 @@ func PrepareMsg(releaseEvent *kwrelease.Event) string {
getChangeInAppVersion(releaseEvent),
)

if configDiff := getConfigDiff(releaseEvent); configDiff != "" {
msg += configDiff
}

case kwrelease.ActionPreUninstall:
msg += fmt.Sprintf("🧼 Uninstalling *%s* from namespace *%s* via Helm. ⏳",
releaseEvent.GetAppName(),
Expand All @@ -75,30 +99,27 @@ func PrepareMsg(releaseEvent *kwrelease.Event) string {
)

case kwrelease.ActionPostUpgrade:
msg += fmt.Sprintf("⏫ Upgraded *%s* from version %s to version *%s* in namespace *%s* via Helm. ✅\n\n```%s```",
msg += fmt.Sprintf("⏫ Upgraded *%s* from version %s to version *%s* in namespace *%s* via Helm. ✅",
releaseEvent.GetAppName(),
releaseEvent.GetPreviousChartVersion(),
releaseEvent.GetChartVersion(),
releaseEvent.GetNamespace(),
releaseEvent.GetNotes(),
)

case kwrelease.ActionPostRollback:
msg += fmt.Sprintf("⏬ Rolled back *%s* from version %s to version *%s* in namespace *%s* via Helm. ✅\n\n```%s```",
msg += fmt.Sprintf("⏬ Rolled back *%s* from version %s to version *%s* in namespace *%s* via Helm. ✅",
releaseEvent.GetAppName(),
releaseEvent.GetPreviousChartVersion(),
releaseEvent.GetChartVersion(),
releaseEvent.GetNamespace(),
releaseEvent.GetNotes(),
)

case kwrelease.ActionPostReplace:
msg += fmt.Sprintf("Replaced *%s* version %s with version *%s* in namespace *%s* via Helm. ✅\n\n```%s```",
msg += fmt.Sprintf("Replaced *%s* version %s with version *%s* in namespace *%s* via Helm. ✅",
releaseEvent.GetAppName(),
releaseEvent.GetPreviousChartVersion(),
releaseEvent.GetChartVersion(),
releaseEvent.GetNamespace(),
releaseEvent.GetNotes(),
)

case kwrelease.ActionFailedInstall:
Expand Down