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: add send to slack support #77

Merged
merged 18 commits into from
Sep 27, 2023
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
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,14 @@ Kor provides various subcommands to identify and list unused resources. The avai

### Supported Flags
```
-e, --exclude-namespaces string Namespaces to be excluded, splited by comma. Example: --exclude-namespace ns1,ns2,ns3. If --include-namespace is set, --exclude-namespaces will be ignored.
-e, --exclude-namespaces string Namespaces to be excluded, split by comma. Example: --exclude-namespace ns1,ns2,ns3. If --include-namespace is set, --exclude-namespaces will be ignored.
-h, --help help for kor
-n, --include-namespaces string Namespaces to run on, split by comma. Example: --include-namespace ns1,ns2,ns3.
-n, --include-namespaces string Namespaces to run on, split by comma. Example: --include-namespace ns1,ns2,ns3.
-k, --kubeconfig string Path to kubeconfig file (optional)
--output string Output format ("table" or "json") (default "table")
--output string Output format (table or json) (default "table")
--slack-auth-token string Slack auth token to send notifications to. --slack-auth-token requires --slack-channel to be set.
--slack-channel string Slack channel to send notifications to. --slack-channel requires --slack-auth-token to be set.
--slack-webhook-url string Slack webhook URL to send notifications to
```

To use a specific subcommand, run `kor [subcommand] [flags]`.
Expand Down Expand Up @@ -135,6 +138,7 @@ helm upgrade -i kor \
--set cronJob.slackToken=<slack-token> \
./charts/kor
```
> Note: To send it to Slack as a file it's required to set the `slackToken` and `slackChannel` values.

It's set to run every Monday at 1 a.m. by default. You can change the schedule by setting the `cronJob.schedule` value.

Expand Down
16 changes: 14 additions & 2 deletions charts/kor/templates/cronjob.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,21 @@ spec:
image: {{ .Values.cronJob.image.repository }}:{{ .Values.cronJob.image.tag }}
command: ["/bin/sh", "-c"]
{{- if .Values.cronJob.slackWebhookUrl }}
args: ["{{ .Values.cronJob.command }} --slack-webhook-url {{ .Values.cronJob.slackWebhookUrl }}"]
args: ["{{ .Values.cronJob.command }} --slack-webhook-url $SLACK_WEBHOOK_URL"]
env:
- name: SLACK_WEBHOOK_URL
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-slack-webhook-url-secret
key: slack-webhook-url
{{- else if and .Values.cronJob.slackChannel .Values.cronJob.slackAuthToken }}
args: ["{{ .Values.cronJob.command }} --slack-channel {{ .Values.cronJob.slackChannel }} --slack-auth-token {{ .Values.cronJob.slackAuthToken }}"]
args: ["{{ .Values.cronJob.command }} --slack-channel {{ .Values.cronJob.slackChannel }} --slack-auth-token $SLACK_AUTH_TOKEN"]
env:
- name: SLACK_AUTH_TOKEN
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-slack-auth-token-secret
key: slack-auth-token
{{- else }}
args: ["{{ .Values.cronJob.command }}"]
{{- end }}
Expand Down
16 changes: 16 additions & 0 deletions charts/kor/templates/secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-slack-auth-token-secret
type: Opaque
data:
slack-auth-token: {{ .Values.cronJob.slackAuthToken | b64enc | quote }}

---
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-slack-webhook-url-secret
type: Opaque
data:
slack-webhook-url: {{ .Values.cronJob.slackWebhookUrl | b64enc | quote }}
2 changes: 1 addition & 1 deletion cmd/kor/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var allCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedAll(includeExcludeLists, clientset)
kor.GetUnusedAll(includeExcludeLists, clientset, slackOpts)
}

},
Expand Down
2 changes: 1 addition & 1 deletion cmd/kor/configmaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var configmapCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedConfigmaps(includeExcludeLists, clientset)
kor.GetUnusedConfigmaps(includeExcludeLists, clientset, slackOpts)
}

},
Expand Down
2 changes: 1 addition & 1 deletion cmd/kor/deployments.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var deployCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedDeployments(includeExcludeLists, clientset)
kor.GetUnusedDeployments(includeExcludeLists, clientset, slackOpts)
}

},
Expand Down
2 changes: 1 addition & 1 deletion cmd/kor/hpas.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var hpaCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedHpas(includeExcludeLists, clientset)
kor.GetUnusedHpas(includeExcludeLists, clientset, slackOpts)
}
},
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/kor/ingresses.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var ingressCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedIngresses(includeExcludeLists, clientset)
kor.GetUnusedIngresses(includeExcludeLists, clientset, slackOpts)
}
},
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/kor/pdbs.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var pdbCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedPdbs(includeExcludeLists, clientset)
kor.GetUnusedPdbs(includeExcludeLists, clientset, slackOpts)
}

},
Expand Down
2 changes: 1 addition & 1 deletion cmd/kor/pvc.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var pvcCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedPvcs(includeExcludeLists, clientset)
kor.GetUnusedPvcs(includeExcludeLists, clientset, slackOpts)
}
},
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/kor/roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var roleCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedRoles(includeExcludeLists, clientset)
kor.GetUnusedRoles(includeExcludeLists, clientset, slackOpts)
}

},
Expand Down
6 changes: 5 additions & 1 deletion cmd/kor/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var rootCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedMulti(includeExcludeLists, kubeconfig, resourceNames)
kor.GetUnusedMulti(includeExcludeLists, kubeconfig, resourceNames, slackOpts)
}
} else {
fmt.Printf("Subcommand %q was not found, try using 'kor --help' for available subcommands", args[0])
Expand All @@ -39,13 +39,17 @@ var (
outputFormat string
kubeconfig string
includeExcludeLists kor.IncludeExcludeLists
slackOpts kor.SlackOpts
)

func Execute() {
rootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "Path to kubeconfig file (optional)")
rootCmd.PersistentFlags().StringVarP(&includeExcludeLists.IncludeListStr, "include-namespaces", "n", "", "Namespaces to run on, splited by comma. Example: --include-namespace ns1,ns2,ns3. ")
rootCmd.PersistentFlags().StringVarP(&includeExcludeLists.ExcludeListStr, "exclude-namespaces", "e", "", "Namespaces to be excluded, splited by comma. Example: --exclude-namespace ns1,ns2,ns3. If --include-namespace is set, --exclude-namespaces will be ignored.")
rootCmd.PersistentFlags().StringVar(&outputFormat, "output", "table", "Output format (table or json)")
rootCmd.PersistentFlags().StringVar(&slackOpts.WebhookURL, "slack-webhook-url", "", "Slack webhook URL to send notifications to")
rootCmd.PersistentFlags().StringVar(&slackOpts.Channel, "slack-channel", "", "Slack channel to send notifications to. --slack-channel requires --slack-auth-token to be set.")
rootCmd.PersistentFlags().StringVar(&slackOpts.Token, "slack-auth-token", "", "Slack auth token to send notifications to. --slack-auth-token requires --slack-channel to be set.")
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "Error while executing your CLI '%s'", err)
os.Exit(1)
Expand Down
2 changes: 1 addition & 1 deletion cmd/kor/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var secretCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedSecrets(includeExcludeLists, clientset)
kor.GetUnusedSecrets(includeExcludeLists, clientset, slackOpts)
}

},
Expand Down
2 changes: 1 addition & 1 deletion cmd/kor/serviceaccounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var serviceAccountCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedServiceAccounts(includeExcludeLists, clientset)
kor.GetUnusedServiceAccounts(includeExcludeLists, clientset, slackOpts)
}

},
Expand Down
2 changes: 1 addition & 1 deletion cmd/kor/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var serviceCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedServices(includeExcludeLists, clientset)
kor.GetUnusedServices(includeExcludeLists, clientset, slackOpts)
}

},
Expand Down
2 changes: 1 addition & 1 deletion cmd/kor/statefulsets.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var stsCmd = &cobra.Command{
fmt.Println(response)
}
} else {
kor.GetUnusedStatefulSets(includeExcludeLists, clientset)
kor.GetUnusedStatefulSets(includeExcludeLists, clientset, slackOpts)
}

},
Expand Down
21 changes: 18 additions & 3 deletions pkg/kor/all.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kor

import (
"bytes"
"encoding/json"
"fmt"
"os"
Expand Down Expand Up @@ -118,8 +119,11 @@ func getUnusedPdbs(clientset kubernetes.Interface, namespace string) ResourceDif
return namespacePdbDiff
}

func GetUnusedAll(includeExcludeLists IncludeExcludeLists, clientset kubernetes.Interface) {
func GetUnusedAll(includeExcludeLists IncludeExcludeLists, clientset kubernetes.Interface, slackOpts SlackOpts) {
namespaces := SetNamespaceList(includeExcludeLists, clientset)

var outputBuffer bytes.Buffer

for _, namespace := range namespaces {
var allDiffs []ResourceDiff
namespaceCMDiff := getUnusedCMs(clientset, namespace)
Expand All @@ -144,9 +148,20 @@ func GetUnusedAll(includeExcludeLists IncludeExcludeLists, clientset kubernetes.
allDiffs = append(allDiffs, namespaceIngressDiff)
namespacePdbDiff := getUnusedPdbs(clientset, namespace)
allDiffs = append(allDiffs, namespacePdbDiff)

output := FormatOutputAll(namespace, allDiffs)
fmt.Println(output)
fmt.Println()

outputBuffer.WriteString(output)
outputBuffer.WriteString("\n")
}

if slackOpts != (SlackOpts{}) {
if err := SendToSlack(SlackMessage{}, slackOpts, outputBuffer.String()); err != nil {
fmt.Fprintf(os.Stderr, "Failed to send message to slack: %v\n", err)
os.Exit(1)
}
} else {
fmt.Println(outputBuffer.String())
}
}

Expand Down
19 changes: 16 additions & 3 deletions pkg/kor/confimgmaps.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kor

import (
"bytes"
"context"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -128,18 +129,30 @@ func processNamespaceCM(clientset kubernetes.Interface, namespace string) ([]str

}

func GetUnusedConfigmaps(includeExcludeLists IncludeExcludeLists, clientset kubernetes.Interface) {
func GetUnusedConfigmaps(includeExcludeLists IncludeExcludeLists, clientset kubernetes.Interface, slackOpts SlackOpts) {
namespaces := SetNamespaceList(includeExcludeLists, clientset)

var outputBuffer bytes.Buffer

for _, namespace := range namespaces {
diff, err := processNamespaceCM(clientset, namespace)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to process namespace %s: %v\n", namespace, err)
continue
}
output := FormatOutput(namespace, diff, "Config Maps")
fmt.Println(output)
fmt.Println()

outputBuffer.WriteString(output)
outputBuffer.WriteString("\n")
}

if slackOpts != (SlackOpts{}) {
if err := SendToSlack(SlackMessage{}, slackOpts, outputBuffer.String()); err != nil {
fmt.Fprintf(os.Stderr, "Failed to send message to slack: %v\n", err)
os.Exit(1)
}
} else {
fmt.Println(outputBuffer.String())
}
}

Expand Down
19 changes: 16 additions & 3 deletions pkg/kor/deployments.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kor

import (
"bytes"
"context"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -32,18 +33,30 @@ func ProcessNamespaceDeployments(clientset kubernetes.Interface, namespace strin
return deploymentsWithoutReplicas, nil
}

func GetUnusedDeployments(includeExcludeLists IncludeExcludeLists, clientset kubernetes.Interface) {
func GetUnusedDeployments(includeExcludeLists IncludeExcludeLists, clientset kubernetes.Interface, slackOpts SlackOpts) {
namespaces := SetNamespaceList(includeExcludeLists, clientset)

var outputBuffer bytes.Buffer

for _, namespace := range namespaces {
diff, err := ProcessNamespaceDeployments(clientset, namespace)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to process namespace %s: %v\n", namespace, err)
continue
}
output := FormatOutput(namespace, diff, "Deployments")
fmt.Println(output)
fmt.Println()

outputBuffer.WriteString(output)
outputBuffer.WriteString("\n")
}

if slackOpts != (SlackOpts{}) {
if err := SendToSlack(SlackMessage{}, slackOpts, outputBuffer.String()); err != nil {
fmt.Fprintf(os.Stderr, "Failed to send message to slack: %v\n", err)
os.Exit(1)
}
} else {
fmt.Println(outputBuffer.String())
}
}

Expand Down
18 changes: 15 additions & 3 deletions pkg/kor/hpas.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kor

import (
"bytes"
"context"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -79,20 +80,31 @@ func processNamespaceHpas(clientset kubernetes.Interface, namespace string) ([]s
return unusedHpas, nil
}

func GetUnusedHpas(includeExcludeLists IncludeExcludeLists, clientset kubernetes.Interface) {
func GetUnusedHpas(includeExcludeLists IncludeExcludeLists, clientset kubernetes.Interface, slackOpts SlackOpts) {
namespaces := SetNamespaceList(includeExcludeLists, clientset)

var outputBuffer bytes.Buffer

for _, namespace := range namespaces {
diff, err := processNamespaceHpas(clientset, namespace)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to process namespace %s: %v\n", namespace, err)
continue
}
output := FormatOutput(namespace, diff, "Hpas")
fmt.Println(output)
fmt.Println()

outputBuffer.WriteString(output)
outputBuffer.WriteString("\n")
}

if slackOpts != (SlackOpts{}) {
if err := SendToSlack(SlackMessage{}, slackOpts, outputBuffer.String()); err != nil {
fmt.Fprintf(os.Stderr, "Failed to send message to slack: %v\n", err)
os.Exit(1)
}
} else {
fmt.Println(outputBuffer.String())
}
}

func GetUnusedHpasStructured(includeExcludeLists IncludeExcludeLists, clientset kubernetes.Interface, outputFormat string) (string, error) {
Expand Down
Loading