Merge pull request #1 from stakater/initial-implementation

[STK-322] Initial implementation
This commit is contained in:
Hazim
2018-07-17 13:50:28 +05:00
committed by GitHub
26 changed files with 957 additions and 1 deletions
+9
View File
@@ -0,0 +1,9 @@
*.swp
_dist/
.idea
golib
release
out/
_gopath/
.DS_Store
vendor
+1
View File
@@ -0,0 +1 @@
0.0.1
Vendored
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/groovy
@Library('github.com/stakater/fabric8-pipeline-library@v2.4.0')
def dummy
goBuildAndRelease {
}
+52
View File
@@ -0,0 +1,52 @@
# note: call scripts from /scripts
.PHONY: default build builder-image binary-image test stop clean-images clean push apply deploy
BUILDER ?= reloader-builder
BINARY ?= Reloader
DOCKER_IMAGE ?= stakater/reloader
# Default value "dev"
DOCKER_TAG ?= 1.0.0
REPOSITORY = ${DOCKER_IMAGE}:${DOCKER_TAG}
VERSION=$(shell cat .version)
BUILD=
GOCMD = go
GLIDECMD = glide
GOFLAGS ?= $(GOFLAGS:)
LDFLAGS =
default: build test
install:
"$(GLIDECMD)" install
build:
"$(GOCMD)" build ${GOFLAGS} ${LDFLAGS} -o "${BINARY}"
builder-image:
@docker build --network host -t "${BUILDER}" -f build/package/Dockerfile.build .
binary-image: builder-image
@docker run --network host --rm "${BUILDER}" | docker build --network host -t "${REPOSITORY}" -f Dockerfile.run -
test:
"$(GOCMD)" test -v ./...
stop:
@docker stop "${BINARY}"
clean-images: stop
@docker rmi "${BUILDER}" "${BINARY}"
clean:
"$(GOCMD)" clean -i
push: ## push the latest Docker image to DockerHub
docker push $(REPOSITORY)
apply:
kubectl apply -f deployments/manifests/ -n temp-reloader
deploy: binary-image push apply
+96 -1
View File
@@ -1 +1,96 @@
# Reloader
# RELOADER
## WHY NAME RELOADER
In english language, Reloader is a thing/tool that can reload certain stuff. So refereig to that meaning relaoder can reload
## Problem
We would like to watch if some change happens in `ConfigMap` and `Secret` objects and then perform certain upgrade on relavent `Deployment`, `Deamonset` and `Statefulset`
## Solution
Reloader can watch any changes in `ConfigMap` and `Secret` objects and update or recreate Pods for their associated `Deployments`, `Deamonsets` and `Statefulsets`. In this way Pods can get the latest changes in `ConfigMap` or `Secret` objects.
**NOTE:** This controller has been inspired from [configmapController](https://github.com/fabric8io/configmapcontroller)
## How to use Reloader
For a `Deployment` called `foo` have a `ConfigMap` called `foo`. Then add this annotation to your `Deployment`
```yaml
metadata:
annotations:
reloader.stakater.com/update-on-change: "foo"
```
Then, providing `Reloader` is running, whenever you edit the `ConfigMap` called `foo` the Reloader will update the `Deployment` by adding the environment variable:
```
STAKATER_FOO_REVISION=${reloaderRevision}
```
This then triggers a rolling upgrade of your deployment's pods to use the new configuration.
Same procedure can be followed to perform rolling upgrade on `Deamonsets` and `Statefulsets` as well.
## Deploying to Kubernetes
You can deploy Reloader by running the following kubectl commands:
```bash
kubectl apply -f rbac.yaml -n <namespace>
kubectl apply -f deployment.yaml -n <namespace>
```
### Helm Charts
Or alternatively if you configured `helm` on your cluster, you can deploy Reloader via helm chart located under `deployments/kubernetes/chart/reloader` folder.
## Help
**Got a question?**
File a GitHub [issue](https://github.com/stakater/Reloader/issues), or send us an [email](mailto:stakater@gmail.com).
### Talk to us on Slack
Join and talk to us on the #tools-imc channel for discussing Reloader
[![Join Slack](https://stakater.github.io/README/stakater-join-slack-btn.png)](https://stakater-slack.herokuapp.com/)
[![Chat](https://stakater.github.io/README/stakater-chat-btn.png)](https://stakater.slack.com/messages/CAN960CTG/)
## Contributing
### Bug Reports & Feature Requests
Please use the [issue tracker](https://github.com/stakater/Reloader/issues) to report any bugs or file feature requests.
### Developing
PRs are welcome. In general, we follow the "fork-and-pull" Git workflow.
1. **Fork** the repo on GitHub
2. **Clone** the project to your own machine
3. **Commit** changes to your own branch
4. **Push** your work back up to your fork
5. Submit a **Pull request** so that we can review your changes
NOTE: Be sure to merge the latest from "upstream" before making a pull request!
## Changelog
View our closed [Pull Requests](https://github.com/stakater/Reloader/pulls?q=is%3Apr+is%3Aclosed).
## License
Apache2 © [Stakater](http://stakater.com)
## About
`Reloader` is maintained by [Stakater][website]. Like it? Please let us know at <hello@stakater.com>
See [our other projects][community]
or contact us in case of professional services and queries on <hello@stakater.com>
[website]: http://stakater.com/
[community]: https://github.com/stakater/
+21
View File
@@ -0,0 +1,21 @@
FROM stakater/go-glide:1.9.3
MAINTAINER "Stakater Team"
RUN apk update
RUN apk -v --update \
add git build-base && \
rm -rf /var/cache/apk/* && \
mkdir -p "$GOPATH/src/github.com/stakater/Reloader"
ADD . "$GOPATH/src/github.com/stakater/Reloader"
RUN cd "$GOPATH/src/github.com/stakater/Reloader" && \
glide update && \
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a --installsuffix cgo --ldflags="-s" -o /Reloader
COPY build/package/Dockerfile.run /
# Running this image produces a tarball suitable to be piped into another
# Docker build command.
CMD tar -cf - -C / Dockerfile.run Reloader
+8
View File
@@ -0,0 +1,8 @@
FROM alpine:3.4
MAINTAINER "Stakater Team"
RUN apk add --update ca-certificates
COPY Reloader /bin/Reloader
ENTRYPOINT ["/bin/Reloader"]
@@ -0,0 +1,13 @@
# Generated from deployments/kubernetes/templates/chart/Chart.yaml.tmpl
apiVersion: v1
name: reloader
description: Reloader chart that runs on kubernetes
version: 0.0.1
keywords:
- Reloader
- kubernetes
home: https://github.com/stakater/Reloader
maintainers:
- name: Stakater
email: hello@stakater.com
@@ -0,0 +1,33 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "reloader-name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" | lower -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "reloader-fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "reloader-labels.selector" -}}
app: {{ template "reloader-name" . }}
group: {{ .Values.reloader.labels.group }}
provider: {{ .Values.reloader.labels.provider }}
{{- end -}}
{{- define "reloader-labels.stakater" -}}
{{ template "reloader-labels.selector" . }}
version: {{ .Values.reloader.labels.version }}
{{- end -}}
{{- define "reloader-labels.chart" -}}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: {{ .Release.Name | quote }}
heritage: {{ .Release.Service | quote }}
{{- end -}}
@@ -0,0 +1,29 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
{{ include "reloader-labels.stakater" . | indent 4 }}
{{ include "reloader-labels.chart" . | indent 4 }}
name: {{ template "reloader-name" . }}
spec:
replicas: 1
revisionHistoryLimit: 2
selector:
matchLabels:
{{ include "reloader-labels.selector" . | indent 6 }}
template:
metadata:
labels:
{{ include "reloader-labels.selector" . | indent 8 }}
spec:
containers:
- env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: "{{ .Values.reloader.image.name }}:{{ .Values.reloader.image.tag }}"
imagePullPolicy: {{ .Values.reloader.image.pullPolicy }}
name: {{ template "reloader-name" . }}
serviceAccountName: {{ template "reloader-name" . }}
@@ -0,0 +1,43 @@
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
{{ include "reloader-labels.stakater" . | indent 4 }}
{{ include "reloader-labels.chart" . | indent 4 }}
name: {{ template "reloader-name" . }}
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
labels:
{{ include "reloader-labels.stakater" . | indent 4 }}
{{ include "reloader-labels.chart" . | indent 4 }}
name: {{ template "reloader-name" . }}-role
namespace: {{ .Release.Namespace }}
rules:
- apiGroups:
- ""
resources:
- secrets
- configmaps
verbs:
- list
- get
- watch
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
labels:
{{ include "reloader-labels.stakater" . | indent 4 }}
{{ include "reloader-labels.chart" . | indent 4 }}
name: {{ template "reloader-name" . }}-role-binding
namespace: {{ .Release.Namespace }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ template "reloader-name" . }}-role
subjects:
- kind: ServiceAccount
name: {{ template "reloader-name" . }}
namespace: {{ .Release.Namespace }}
@@ -0,0 +1,14 @@
# Generated from deployments/kubernetes/templates/chart/values.yaml.tmpl
kubernetes:
host: https://kubernetes.default
reloader:
labels:
provider: stakater
group: com.stakater.platform
version: 0.0.1
image:
name: stakater/reloader
tag: "0.0.1"
pullPolicy: IfNotPresent
@@ -0,0 +1,13 @@
# Generated from deployments/kubernetes/templates/chart/Chart.yaml.tmpl
apiVersion: v1
name: reloader
description: Reloader chart that runs on kubernetes
version: {{ getenv "VERSION" }}
keywords:
- Reloader
- kubernetes
home: https://github.com/stakater/Reloader
maintainers:
- name: Stakater
email: hello@stakater.com
@@ -0,0 +1,14 @@
# Generated from deployments/kubernetes/templates/chart/values.yaml.tmpl
kubernetes:
host: https://kubernetes.default
reloader:
labels:
provider: stakater
group: com.stakater.platform
version: {{ getenv "VERSION" }}
image:
name: {{ getenv "DOCKER_IMAGE" }}
tag: "{{ getenv "VERSION" }}"
pullPolicy: IfNotPresent
+14
View File
@@ -0,0 +1,14 @@
package: github.com/stakater/Reloader
import:
- package: k8s.io/api
version: kubernetes-1.8.0
- package: k8s.io/apimachinery
version: kubernetes-1.8.0
- package: k8s.io/client-go
version: 5.0.0
- package: github.com/spf13/cobra
version: 0.0.3
- package: github.com/spf13/pflag
version: 1.0.1
- package: github.com/sirupsen/logrus
version: 1.0.5
+9
View File
@@ -0,0 +1,9 @@
package app
import "github.com/stakater/Reloader/internal/pkg/cmd"
// Run runs the command
func Run() error {
cmd := cmd.NewReloaderCommand()
return cmd.Execute()
}
+52
View File
@@ -0,0 +1,52 @@
package cmd
import (
"os"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/stakater/Reloader/internal/pkg/controller"
"github.com/stakater/Reloader/pkg/kube"
"k8s.io/apimachinery/pkg/apis/meta/v1"
)
// NewReloaderCommand starts the reloader controller
func NewReloaderCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "reloader",
Short: "A watcher for your Kubernetes cluster",
Run: startReloader,
}
return cmd
}
func startReloader(cmd *cobra.Command, args []string) {
logrus.Info("Starting Reloader")
currentNamespace := os.Getenv("KUBERNETES_NAMESPACE")
if len(currentNamespace) == 0 {
currentNamespace = v1.NamespaceAll
logrus.Warnf("KUBERNETES_NAMESPACE is unset, will detect changes in all namespaces.")
}
// create the clientset
clientset, err := kube.GetClient()
if err != nil {
logrus.Fatal(err)
}
for k := range kube.ResourceMap {
c, err := controller.NewController(clientset, k, currentNamespace)
if err != nil {
logrus.Fatalf("%s", err)
}
// Now let's start the controller
stop := make(chan struct{})
defer close(stop)
go c.Run(1, stop)
}
// Wait forever
select {}
}
+144
View File
@@ -0,0 +1,144 @@
package controller
import (
"fmt"
"time"
"github.com/sirupsen/logrus"
"github.com/stakater/Reloader/internal/pkg/handler"
"github.com/stakater/Reloader/pkg/kube"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/runtime"
errorHandler "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
)
// Controller for checking events
type Controller struct {
client kubernetes.Interface
indexer cache.Indexer
queue workqueue.RateLimitingInterface
informer cache.Controller
namespace string
}
// NewController for initializing a Controller
func NewController(
client kubernetes.Interface, resource string, namespace string) (*Controller, error) {
c := Controller{
client: client,
namespace: namespace,
}
queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
listWatcher := cache.NewListWatchFromClient(client.CoreV1().RESTClient(), resource, namespace, fields.Everything())
indexer, informer := cache.NewIndexerInformer(listWatcher, kube.ResourceMap[resource], 0, cache.ResourceEventHandlerFuncs{
AddFunc: c.Add,
UpdateFunc: c.Update,
DeleteFunc: c.Delete,
}, cache.Indexers{})
c.indexer = indexer
c.informer = informer
c.queue = queue
return &c, nil
}
// Add function to add a new object to the queue in case of creating a resource
func (c *Controller) Add(obj interface{}) {
c.queue.Add(handler.ResourceCreatedHandler{
Resource: obj,
})
}
// Update function to add an old object and a new object to the queue in case of updating a resource
func (c *Controller) Update(old interface{}, new interface{}) {
c.queue.Add(handler.ResourceUpdatedHandler{
Resource: new,
OldResource: old,
})
}
// Delete function to add an object to the queue in case of deleting a resource
func (c *Controller) Delete(old interface{}) {
// TODO Added this function for future usecase
logrus.Infof("Deleted resource has been detected but no further implementation found to take action")
}
//Run function for controller which handles the queue
func (c *Controller) Run(threadiness int, stopCh chan struct{}) {
logrus.Infof("Starting Controller")
defer errorHandler.HandleCrash()
// Let the workers stop when we are done
defer c.queue.ShutDown()
go c.informer.Run(stopCh)
// Wait for all involved caches to be synced, before processing items from the queue is started
if !cache.WaitForCacheSync(stopCh, c.informer.HasSynced) {
errorHandler.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
return
}
for i := 0; i < threadiness; i++ {
go wait.Until(c.runWorker, time.Second, stopCh)
}
<-stopCh
logrus.Infof("Stopping Controller")
}
func (c *Controller) runWorker() {
for c.processNextItem() {
}
}
func (c *Controller) processNextItem() bool {
// Wait until there is a new item in the working queue
resourceHandler, quit := c.queue.Get()
if quit {
return false
}
// Tell the queue that we are done with processing this key. This unblocks the key for other workers
// This allows safe parallel processing because two events with the same key are never processed in
// parallel.
defer c.queue.Done(resourceHandler)
// Invoke the method containing the business logic
err := resourceHandler.(handler.ResourceHandler).Handle()
// Handle the error if something went wrong during the execution of the business logic
c.handleErr(err, resourceHandler)
return true
}
// handleErr checks if an error happened and makes sure we will retry later.
func (c *Controller) handleErr(err error, key interface{}) {
if err == nil {
// Forget about the #AddRateLimited history of the key on every successful synchronization.
// This ensures that future processing of updates for this key is not delayed because of
// an outdated error history.
c.queue.Forget(key)
return
}
// This controller retries 5 times if something goes wrong. After that, it stops trying.
if c.queue.NumRequeues(key) < 5 {
logrus.Errorf("Error syncing events %v: %v", key, err)
// Re-enqueue the key rate limited. Based on the rate limiter on the
// queue and the re-enqueue history, the key will be processed later again.
c.queue.AddRateLimited(key)
return
}
c.queue.Forget(key)
// Report to an external entity that, even after several retries, we could not successfully process this key
runtime.HandleError(err)
logrus.Infof("Dropping the key %q out of the queue: %v", key, err)
}
+254
View File
@@ -0,0 +1,254 @@
package controller
import (
"math/rand"
"testing"
"time"
"github.com/sirupsen/logrus"
"github.com/stakater/Reloader/pkg/kube"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
var (
configmapNamePrefix = "testconfigmap-reloader"
secretNamePrefix = "testsecret-reloader"
letters = []rune("abcdefghijklmnopqrstuvwxyz")
)
func randSeq(n int) string {
rand.Seed(time.Now().UnixNano())
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
// Creating a Controller to do a rolling upgrade upon updating the configmap or secret
func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) {
client, err := kube.GetClient()
if err != nil {
logrus.Errorf("Unable to create Kubernetes client error = %v", err)
return
}
namespace := "test-reloader"
createNamespace(t, namespace, client)
defer deleteNamespace(t, namespace, client)
controller, err := NewController(client, "configMaps", namespace)
if err != nil {
logrus.Errorf("Unable to create NewController error = %v", err)
return
}
stop := make(chan struct{})
defer close(stop)
go controller.Run(1, stop)
time.Sleep(10 * time.Second)
configmapName := configmapNamePrefix + "-update-" + randSeq(5)
configmapClient := client.CoreV1().ConfigMaps(namespace)
_, err = configmapClient.Create(initConfigmap(namespace, configmapName))
if err != nil {
logrus.Fatalf("Fatal error in configmap creation: %v", err)
}
logrus.Infof("Created Configmap %q.\n", configmapName)
time.Sleep(10 * time.Second)
deployment := createDeployement(configmapName, namespace, client)
logrus.Infof("Updating Configmap %q.\n", configmapName)
_, err = configmapClient.Get(configmapName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Error while getting configmap %v", err)
}
_, updateErr := configmapClient.Update(updateConfigmap(namespace, configmapName))
// TODO: Add functionality to verify reloader functionality here
if updateErr != nil {
err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{})
if err != nil {
logrus.Errorf("Error while deleting the configmap %v", err)
}
logrus.Fatalf("Fatal error in configmap update: %v", updateErr)
}
time.Sleep(10 * time.Second)
logrus.Infof("Deleting Deployment %q.\n", deployment.GetObjectMeta().GetName())
deploymentError := controller.client.ExtensionsV1beta1().Deployments(namespace).Delete(configmapName, &metav1.DeleteOptions{})
if deploymentError != nil {
logrus.Fatalf("Error while deleting the configmap %v", deploymentError)
}
logrus.Infof("Deleting Configmap %q.\n", configmapName)
err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{})
if err != nil {
logrus.Errorf("Error while deleting the configmap %v", err)
}
time.Sleep(15 * time.Second)
}
func createDeployement(deploymentName string, namespace string, client kubernetes.Interface) *v1beta1.Deployment {
deploymentClient := client.ExtensionsV1beta1().Deployments(namespace)
deployment := initDeployment(namespace, deploymentName)
deployment, err := deploymentClient.Create(deployment)
if err != nil {
logrus.Fatalf("Fatal error in deployment creation: %v", err)
}
logrus.Infof("Created Deployment %q.\n", deployment.GetObjectMeta().GetName())
return deployment
}
func TestControllerForUpdatingSecretShouldUpdateDeployment(t *testing.T) {
client, err := kube.GetClient()
if err != nil {
logrus.Errorf("Unable to create Kubernetes client error = %v", err)
return
}
namespace := "test-reloader-secrets"
createNamespace(t, namespace, client)
defer deleteNamespace(t, namespace, client)
controller, err := NewController(client, "secrets", namespace)
if err != nil {
logrus.Errorf("Unable to create NewController error = %v", err)
return
}
stop := make(chan struct{})
defer close(stop)
go controller.Run(1, stop)
time.Sleep(10 * time.Second)
secretName := secretNamePrefix + "-update-" + randSeq(5)
secretClient := client.CoreV1().Secrets(namespace)
_, err = secretClient.Create(initSecret(namespace, secretName))
if err != nil {
logrus.Fatalf("Fatal error in secret creation: %v", err)
}
logrus.Infof("Created Secret %q.\n", secretName)
time.Sleep(10 * time.Second)
logrus.Infof("Updating Secret %q.\n", secretName)
_, err = secretClient.Get(secretName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Error while getting secret %v", err)
}
_, updateErr := secretClient.Update(updateSecret(namespace, secretName))
// TODO: Add functionality to verify reloader functionality here
if updateErr != nil {
err := controller.client.CoreV1().Secrets(namespace).Delete(secretName, &metav1.DeleteOptions{})
if err != nil {
logrus.Errorf("Error while deleting the secret %v", err)
}
logrus.Errorf("Error while updating the secret %v", err)
}
time.Sleep(10 * time.Second)
logrus.Infof("Deleting Secret %q.\n", secretName)
err = controller.client.CoreV1().Secrets(namespace).Delete(secretName, &metav1.DeleteOptions{})
if err != nil {
logrus.Errorf("Error while deleting the secret %v", err)
}
time.Sleep(15 * time.Second)
}
func initConfigmap(namespace string, configmapName string) *v1.ConfigMap {
return &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: configmapName,
Namespace: namespace,
Labels: map[string]string{"firstLabel": "temp"},
},
Data: map[string]string{"test.url": "www.google.com"},
}
}
func initDeployment(namespace string, deploymentName string) *v1beta1.Deployment {
replicaset := int32(1)
return &v1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: deploymentName,
Namespace: namespace,
Labels: map[string]string{"firstLabel": "temp"},
Annotations: map[string]string{"reloader.stakater.com/update-on-change": deploymentName},
},
Spec: v1beta1.DeploymentSpec{
Replicas: &replicaset,
Strategy: v1beta1.DeploymentStrategy{
Type: v1beta1.RollingUpdateDeploymentStrategyType,
},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"secondLabel": "temp"},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Image: "tutum/hello-world",
Name: deploymentName,
Env: []v1.EnvVar{
{
Name: "BUCKET_NAME",
Value: "test",
},
},
},
},
},
},
},
}
}
func initSecret(namespace string, secretName string) *v1.Secret {
return &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
Labels: map[string]string{"firstLabel": "temp"},
},
Data: map[string][]byte{"test.url": []byte("dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=")},
}
}
func createNamespace(t *testing.T, namespace string, client kubernetes.Interface) {
_, err := client.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}})
if err != nil {
t.Error("Failed to create namespace for testing", err)
} else {
logrus.Infof("Creating namespace for testing = %s", namespace)
}
}
func deleteNamespace(t *testing.T, namespace string, client kubernetes.Interface) {
err := client.CoreV1().Namespaces().Delete(namespace, &metav1.DeleteOptions{})
if err != nil {
t.Error("Failed to delete namespace that was created for testing", err)
} else {
logrus.Infof("Deleting namespace for testing = %s", namespace)
}
}
func updateConfigmap(namespace string, configmapName string) *v1.ConfigMap {
return &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: configmapName,
Namespace: namespace,
Labels: map[string]string{"firstLabel": "temp"},
},
Data: map[string]string{"test.url": "www.stakater.com"},
}
}
func updateSecret(namespace string, secretName string) *v1.Secret {
return &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: namespace,
Labels: map[string]string{"firstLabel": "temp"},
},
Data: map[string][]byte{"test.url": []byte("dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy")},
}
}
+29
View File
@@ -0,0 +1,29 @@
package handler
import (
"github.com/sirupsen/logrus"
"k8s.io/api/core/v1"
)
// ResourceCreatedHandler contains new objects
type ResourceCreatedHandler struct {
Resource interface{}
}
// Handle processes the newly created resource
func (r ResourceCreatedHandler) Handle() error {
if r.Resource == nil {
logrus.Errorf("Error in Handler")
} else {
logrus.Infof("Detected changes in object %s", r.Resource)
// process resource based on its type
if _, ok := r.Resource.(*v1.ConfigMap); ok {
logrus.Infof("Performing 'Added' action for resource of type 'configmap'")
} else if _, ok := r.Resource.(*v1.Secret); ok {
logrus.Infof("Performing 'Added' action for resource of type 'secret'")
} else {
logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found %v", r.Resource)
}
}
return nil
}
+6
View File
@@ -0,0 +1,6 @@
package handler
// ResourceHandler handles the creation and update of resources
type ResourceHandler interface {
Handle() error
}
+30
View File
@@ -0,0 +1,30 @@
package handler
import (
"github.com/sirupsen/logrus"
"k8s.io/api/core/v1"
)
// ResourceUpdatedHandler contains updated objects
type ResourceUpdatedHandler struct {
Resource interface{}
OldResource interface{}
}
// Handle processes the updated resource
func (r ResourceUpdatedHandler) Handle() error {
if r.Resource == nil || r.OldResource == nil {
logrus.Errorf("Error in Handler")
} else {
logrus.Infof("Detected changes in object %s", r.Resource)
// process resource based on its type
if _, ok := r.Resource.(*v1.ConfigMap); ok {
logrus.Infof("Performing 'Updated' action for resource of type 'configmap'")
} else if _, ok := r.Resource.(*v1.Secret); ok {
logrus.Infof("Performing 'Updated' action for resource of type 'secret'")
} else {
logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found %v", r.Resource)
}
}
return nil
}
+14
View File
@@ -0,0 +1,14 @@
package main
import (
"os"
"github.com/stakater/Reloader/internal/pkg/app"
)
func main() {
if err := app.Run(); err != nil {
os.Exit(1)
}
os.Exit(0)
}
+35
View File
@@ -0,0 +1,35 @@
package kube
import (
"os"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
// GetClient gets the client for k8s, if ~/.kube/config exists so get that config else incluster config
func GetClient() (*kubernetes.Clientset, error) {
var config *rest.Config
var err error
kubeconfigPath := os.Getenv("KUBECONFIG")
if kubeconfigPath == "" {
kubeconfigPath = os.Getenv("HOME") + "/.kube/config"
}
//If file exists so use that config settings
if _, err := os.Stat(kubeconfigPath); err == nil {
config, err = clientcmd.BuildConfigFromFlags("", kubeconfigPath)
if err != nil {
return nil, err
}
} else { //Use Incluster Configuration
config, err = rest.InClusterConfig()
if err != nil {
return nil, err
}
}
if err != nil {
return nil, err
}
return kubernetes.NewForConfig(config)
}
+12
View File
@@ -0,0 +1,12 @@
package kube
import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// ResourceMap are resources from where changes are going to be detected
var ResourceMap = map[string]runtime.Object{
"configMaps": &v1.ConfigMap{},
"secrets": &v1.Secret{},
}
+4
View File
@@ -0,0 +1,4 @@
issues:
kind: 1
url: https://aurorasolutions.atlassian.net
project: STK