Skip to content

Commit

Permalink
Add support to print the summary in table format
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilsbhat committed Mar 25, 2023
1 parent dd1ed34 commit 2e2c7ee
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 47 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ vendor/

# dropping local built binaries
helm-drift
helm-drift_*

# dropping the templates
templates-latest/
Expand Down
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ DATE?=$(shell date)
PlATFORM?=$(shell go env GOOS)
ARCHITECTURE?=$(shell go env GOARCH)
GOVERSION?=$(shell go version | awk '{printf $$3}')
TEST_FILES?=$(shell go list ./... | grep -v /vendor/ | grep -v examples)
BUILD_WITH_FLAGS="-s -w -X 'github.com/nikhilsbhat/helm-drift/version.Version=${VERSION}' -X 'github.com/nikhilsbhat/helm-drift/version.Env=${BUILD_ENVIRONMENT}' -X 'github.com/nikhilsbhat/helm-drift/version.BuildDate=${DATE}' -X 'github.com/nikhilsbhat/helm-drift/version.Revision=${REVISION}' -X 'github.com/nikhilsbhat/helm-drift/version.Platform=${PlATFORM}/${ARCHITECTURE}' -X 'github.com/nikhilsbhat/helm-drift/version.GoVersion=${GOVERSION}'"

# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
Expand All @@ -33,8 +34,8 @@ local.check: local.fmt ## Loads all the dependencies to vendor directory
go mod vendor
go mod tidy

local.build: local.check ## Generates the artifact with the help of 'go build'
GOVERSION=${GOVERSION} BUILD_ENVIRONMENT=${BUILD_ENVIRONMENT} goreleaser build --rm-dist
build.local: local.check ## Generates the artifact with the help of 'go build'
@go build -o $(APP_NAME)_v$(VERSION) -ldflags="-s -w"

local.push: local.build ## Pushes built artifact to the specified location

Expand Down Expand Up @@ -68,4 +69,4 @@ generate.document: ## generates cli documents using 'github.com/spf13/cobra/doc'
@go generate github.com/nikhilsbhat/helm-drift/docs

test: ## runs test cases
go test ./... -mod=vendor -coverprofile cover.out
@time go test $(TEST_FILES) -mod=vendor -coverprofile cover.out && go tool cover -html=cover.out -o cover.html && open cover.html
4 changes: 4 additions & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ func registerFlags(cmd *cobra.Command) {
"setting this would set '--skip-tests' for helm template command while generating templates")
cmd.PersistentFlags().StringVarP(&drifts.LogLevel, "log-level", "l", "info",
"log level for the plugin helm drift (defaults to info)")
cmd.PersistentFlags().BoolVarP(&drifts.NoColor, "no-color", "", false,
"enabling this would render summary with no color")
}

// Registers all flags to command, get.
Expand All @@ -36,4 +38,6 @@ func registerRunFlags(cmd *cobra.Command) {
"enable the flag if prerequisite validation needs to be skipped")
cmd.PersistentFlags().BoolVarP(&drifts.SkipClean, "skip-cleaning", "", false,
"enable the flag to skip cleaning the manifests rendered on to disk")
cmd.PersistentFlags().BoolVarP(&drifts.Summary, "summary", "", false,
"if enabled, prints a quick summary in table format without printing actual drifts")
}
1 change: 1 addition & 0 deletions completion.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: drift
flags:
- log-level
- no-color
- set
- set-file
- set-string
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ module github.com/nikhilsbhat/helm-drift
go 1.19

require (
github.com/olekukonko/tablewriter v0.0.5
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1
github.com/stretchr/testify v1.8.1
github.com/thoas/go-funk v0.9.3
gopkg.in/yaml.v3 v3.0.1
helm.sh/helm/v3 v3.11.2
k8s.io/client-go v0.26.0
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ github.com/nelsam/hel/v2 v2.3.2/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy
github.com/nelsam/hel/v2 v2.3.3/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=
github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys=
Expand Down Expand Up @@ -593,6 +594,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw=
github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
Expand Down
56 changes: 42 additions & 14 deletions pkg/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,29 @@ package pkg
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"

"github.com/thoas/go-funk"
)

func (drift *Drift) Diff() (map[string]string, error) {
templatePath := filepath.Join(drift.TempPath, drift.release)
type Deviation struct {
Deviations string
HasDrift bool
Kind string
Resource string
TemplatePath string
ManifestPath string
}

drift.log.Debugf("reading rendered manifsets under '%s' for calculating diff", templatePath)
manifests, err := os.ReadDir(templatePath)
if err != nil {
return nil, err
}
const (
Failed = "FAILED"
Success = "SUCCESS"
)

diffs := make(map[string]string, 0)
for _, manifest := range manifests {
manifestPath := filepath.Join(templatePath, manifest.Name())
func (drift *Drift) Diff(deviations []Deviation) ([]Deviation, error) {
diffs := make([]Deviation, 0)
for _, deviation := range deviations {
manifestPath := deviation.ManifestPath

drift.log.Debugf("calculating diff for %s", manifestPath)

Expand All @@ -39,12 +45,34 @@ func (drift *Drift) Diff() (map[string]string, error) {
}

if len(out) != 0 {
drift.log.Debugf("found diffs for %s", manifest.Name())
diffs[manifestPath] = string(out)
drift.log.Debugf("found diffs for '%s' with name '%s'", deviation.Kind, deviation.Kind)
deviation.HasDrift = true
deviation.Deviations = string(out)
}
diffs = append(diffs, deviation)
}

drift.log.Debug("ran diffs for all manifests successfully")

return diffs, nil
}

func (drift *Drift) status(drifts []Deviation) string {
hasDrift := funk.Contains(drifts, func(dft Deviation) bool {
return dft.HasDrift
})

if hasDrift {
return Failed
}

return Success
}

func (drift *Deviation) hasDrift() string {
if drift.HasDrift {
return "YES"
}

return "NO"
}
24 changes: 19 additions & 5 deletions pkg/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,46 @@ const (
manifestFilePermission = 0o644
)

func (drift *Drift) renderToDisk(manifests []string) error {
func (drift *Drift) renderToDisk(manifests []string) ([]Deviation, error) {
templatePath := filepath.Join(drift.TempPath, drift.release)

drift.log.Debugf("rendering helm manifests to disk under %s", templatePath)
drift.log.Debugf("creating directories '%s' to generate manifests", templatePath)
if err := os.MkdirAll(templatePath, templatePathPermission); err != nil {
return err
return nil, err
}

deviations := make([]Deviation, 0)
for _, manifest := range manifests {
name, err := k8s.NewName().Get(manifest)
if err != nil {
return err
return nil, err
}

kind, err := k8s.NewKind().Get(manifest)
if err != nil {
return nil, err
}

drift.log.Debugf("generating manifest %s", name)

manifestPath := filepath.Join(templatePath, fmt.Sprintf("%s.yaml", name))
if err = os.WriteFile(manifestPath, []byte(manifest), manifestFilePermission); err != nil {
return err
return nil, err
}

deviation := Deviation{
Kind: kind,
Resource: name,
TemplatePath: templatePath,
ManifestPath: manifestPath,
}
deviations = append(deviations, deviation)
}

drift.log.Debug("all manifests rendered to disk successfully")

return nil
return deviations, nil
}

func (drift *Drift) cleanManifests() error {
Expand Down
27 changes: 18 additions & 9 deletions pkg/drift.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"io"
"os"

"github.com/sirupsen/logrus"
)
Expand All @@ -23,28 +24,35 @@ type Drift struct {
SkipTests bool
SkipValidation bool
SkipClean bool
Summary bool
Regex string
LogLevel string
FromRelease bool
NoColor bool
TempPath string
release string
chart string
namespace string
log *logrus.Logger
writer *bufio.Writer
}

// SetRelease sets release for helm drift.
func (drift *Drift) SetRelease(release string) {
drift.release = release
}

// SetChart sets chart name for helm drift.
func (drift *Drift) SetChart(chart string) {
drift.chart = chart
}

// SetWriter sets writer to be used by helm drift.
func (drift *Drift) SetWriter(writer io.Writer) {
drift.writer = bufio.NewWriter(writer)
}

// GetDrift gets all the drifts that the given release/chart has.
func (drift *Drift) GetDrift() error {
if !drift.SkipValidation {
if !drift.validatePrerequisite() {
Expand All @@ -56,14 +64,17 @@ func (drift *Drift) GetDrift() error {
fmt.Sprintf("got all required values to identify drifts from chart/release '%s' proceeding furter to fetch the same", drift.release),
)

drift.setNameSpace()

chart, err := drift.getChartManifests()
if err != nil {
return err
}

kubeKindTemplates := drift.getTemplates(chart)

if err = drift.renderToDisk(kubeKindTemplates); err != nil {
deviations, err := drift.renderToDisk(kubeKindTemplates)
if err != nil {
return err
}

Expand All @@ -73,7 +84,7 @@ func (drift *Drift) GetDrift() error {
}
}(drift)

out, err := drift.Diff()
out, err := drift.Diff(deviations)
if err != nil {
return err
}
Expand All @@ -84,13 +95,7 @@ func (drift *Drift) GetDrift() error {
return nil
}

for file, diff := range out {
drift.render(addNewLine("------------------------------------------------------------------------------------"))
drift.render(addNewLine(addNewLine(fmt.Sprintf("Identified drifts in: '%s'", file))))
drift.render(addNewLine("-----------"))
drift.render(diff)
drift.render(addNewLine(addNewLine("-----------")))
}
drift.render(out)

return nil
}
Expand All @@ -106,3 +111,7 @@ func (drift *Drift) getChartManifests() ([]byte, error) {

return drift.getChartFromTemplate()
}

func (drift *Drift) setNameSpace() {
drift.namespace = os.Getenv(helmNamespace)
}
32 changes: 18 additions & 14 deletions pkg/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,31 @@ import (
const (
helmContext = "HELM_KUBECONTEXT"
helmNamespace = "HELM_NAMESPACE"
kubeContext = "HELM_KUBECONTEXT"
kubeNamespace = "HELM_NAMESPACE"
kubeConfig = "KUBECONFIG"
)

func (drift *Drift) kubeCmd(args ...string) *exec.Cmd {
cmd := exec.CommandContext(context.Background(), "kubectl")
cmd.Env = drift.getKubeEnvironments()
cmd.Args = append(cmd.Args, "diff")
cmd.Args = append(cmd.Args, args...)
cmd.Args = append(cmd.Args, setNamespace())
cmd.Env = drift.getKubeEnvironments()
cmd.Args = append(cmd.Args, drift.getNamespace())

if len(setContext()) != 0 {
cmd.Args = append(cmd.Args, setContext())
}

drift.log.Debugf("running command '%s' to find diff", cmd.String())

return cmd
}

func (drift *Drift) getKubeEnvironments() []string {
contexts := os.Getenv(helmContext)
namespace := os.Getenv(helmNamespace)
config := os.Getenv(kubeConfig)

os.Environ()
var envs []string
if len(contexts) != 0 {
envs = append(envs, constructEnv(kubeContext, contexts))
}
if len(namespace) != 0 {
envs = append(envs, constructEnv(kubeNamespace, namespace))
}

if len(config) != 0 {
envs = append(envs, constructEnv(kubeConfig, config))
} else {
Expand All @@ -58,6 +53,15 @@ func constructEnv(key, value string) string {
return fmt.Sprintf("%s=%s", key, value)
}

func setNamespace() string {
return fmt.Sprintf("-n=%s", os.Getenv(helmNamespace))
func setContext() string {
kubeContext := os.Getenv(helmContext)
if len(kubeContext) != 0 {
return fmt.Sprintf("--kube-context=%s", kubeContext)
}

return kubeContext
}

func (drift *Drift) getNamespace() string {
return fmt.Sprintf("-n=%s", drift.namespace)
}
Loading

0 comments on commit 2e2c7ee

Please sign in to comment.