Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
vitobotta committed Oct 18, 2019
1 parent 03e4f83 commit cb563f8
Show file tree
Hide file tree
Showing 16 changed files with 246 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
bin/*
README.me
current_version
Empty file added .gitignore
Empty file.
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM ruby:2.6.5-slim

RUN apt-get update -qq \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends build-essential git-core

ENV APP_HOME /app
RUN mkdir $APP_HOME
WORKDIR $APP_HOME

COPY Gemfile* ./

RUN gem install bundler \
&& bundle install -j "$(getconf _NPROCESSORS_ONLN)"

COPY . $APP_HOME

CMD ["bundle", "exec", "ruby", "app.rb"]
8 changes: 8 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
source 'https://rubygems.org'

ruby '2.6.5'

gem "slack-notifier"
gem "k8s-client"
gem 'concurrent-ruby', require: 'concurrent'
gem 'logger'
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
# velero-backup-notification
# velero-backup-notification

envsubst
6 changes: 6 additions & 0 deletions app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
require_relative "lib/controller"

K8s::Logging.debug!
K8s::Transport.verbose!

Controller.new.start
8 changes: 8 additions & 0 deletions bin/build
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash

IMAGE="vitobotta/velero-backup-notification"
VERSION="v"`date +%s`""

docker build -t ${IMAGE}:${VERSION} .

echo "${VERSION}" > current_version
6 changes: 6 additions & 0 deletions bin/publish
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

IMAGE="vitobotta/velero-backup-notification"
VERSION="$(cat current_version)"

docker push ${IMAGE}:${VERSION}
1 change: 1 addition & 0 deletions current_version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v1571423030
22 changes: 22 additions & 0 deletions helm/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
5 changes: 5 additions & 0 deletions helm/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v1
appVersion: "1.0"
description: A Helm chart to send email/Slack notifications for Velero backups/restores
name: velero-backup-notification
version: 0.1.0
36 changes: 36 additions & 0 deletions helm/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: velero-backup-notification
spec:
replicas: 1
selector:
matchLabels:
app: velero-backup-notification
template:
metadata:
labels:
app: velero-backup-notification
spec:
serviceAccountName: velero-server
containers:
- name: velero-backup-notification
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: IfNotPresent
env:
- name: VELERO_NAMESPACE
value: {{ .Values.velero_namespace | quote }}
- name: ENABLE_SLACK_NOTIFICATIONS
value: {{ .Values.slack.enabled | quote }}
- name: SLACK_USERNAME
value: {{ .Values.slack.username
- name: SLACK_WEBHOOK
valueFrom:
secretKeyRef:
key: slack_webhook
name: velero-backup-notification-secrets
- name: SLACK_CHANNEL
valueFrom:
secretKeyRef:
key: slack_channel
name: velero-backup-notification-secrets
8 changes: 8 additions & 0 deletions helm/templates/secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: velero-backup-notification-secrets
type: Opaque
stringData:
slack_webhook: {{ .Values.slack.webhook | quote }}
slack_channel: {{ .Values.slack.channel | quote }}
11 changes: 11 additions & 0 deletions helm/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
image:
repository: vitobotta/velero-backup-notification
tag: v1571423030

slack:
enabled: true
webhook: "https://...."
channel: "stuff"
username: Velero

velero_namespace: velero
89 changes: 89 additions & 0 deletions lib/controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
require "bundler/setup"
require "slack-notifier"
require "k8s-client"
require "concurrent"
require 'logger'
require_relative "k8s_client"

class Controller
TIMEOUT = 3600*24*365

def initialize
@velero_namespace = ENV.fetch("VELERO_NAMESPACE", "velero")

@slack = Slack::Notifier.new ENV["SLACK_WEBHOOK"] do
defaults channel: ENV["SLACK_CHANNEL"], username: ENV.fetch("SLACK_USERNAME", "Velero")
end

@k8s_client = Kubernetes::Client.new
@logger = Logger.new(STDOUT)
end

def start
$stdout.sync = true

t1 = Thread.new do
watch_resources :backups
end

t2 = Thread.new do
watch_resources :restores
end

t1.join
t2.join
end

private

attr_reader :velero_namespace, :slack, :k8s_client, :logger

def notify(event)
phase = event.resource.status.phase

return if phase.empty? || phase == "Deleting"

msg = "#{event.resource.kind} #{event.resource.metadata.name} #{phase}"

logger.info msg

if ENV.fetch("ENABLE_SLACK_NOTIFICATIONS", "false") =~ /true/i
at = if phase =~ /failed/i
[:here]
else
[]
end

attachment = {
fallback: msg,
text: msg,
color: phase =~ /failed/i ? "danger" : "good"
}

begin
slack.post at: at, attachments: [attachment]
rescue => e
logger.error "Something went wrong with the Slack notification: #{e.message}"
end
end
end

def watch_resources(resource_type)
resource_version = k8s_client.api("velero.io/v1").resource(resource_type.to_s, namespace: velero_namespace).list.map{|resource| resource.metadata.resourceVersion}.map(&:to_i).max.to_s

begin
logger.info "Watching #{resource_type}..."

k8s_client.api("velero.io/v1").resource(resource_type.to_s, namespace: velero_namespace).watch(timeout: TIMEOUT, resourceVersion: resource_version) do |event|
resource_version = event.resource.metadata.resourceVersion
notify event
end

rescue EOFError, Excon::Error::Socket
logger.info "Reconnecting to API..."
retry
end
end
end


23 changes: 23 additions & 0 deletions lib/k8s_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Kubernetes
class Client
KUBE_CONFIG = ENV.fetch("KUBE_CONFIG", "")
KUBE_HOST = ENV.fetch("KUBE_HOST", "kubernetes.default.svc.cluster.local")
KUBE_PORT = ENV.fetch("KUBE_PORT", "443")

def initialize
@client = KUBE_CONFIG.empty? ? K8s::Client.in_cluster_config : K8s::Client.config(K8s::Config.load_file(KUBE_CONFIG), server: "https://#{KUBE_HOST}:#{KUBE_PORT}")
end

private

attr_reader :client

def method_missing(method, *args)
if client.respond_to?(method)
client.send(method, *args)
else
super
end
end
end
end

0 comments on commit cb563f8

Please sign in to comment.