mirror of
https://github.com/stakater/Reloader.git
synced 2026-08-20 12:36:26 +00:00
Add support for ignoring namespaces
This commit is contained in:
@@ -29,8 +29,7 @@ metadata:
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
template: metadata:
|
||||
```
|
||||
|
||||
This will discover deployments/daemonsets/statefulset automatically where `foo-configmap` or `foo-secret` is being used either via environment variable or from volume mount. And it will perform rolling upgrade on related pods when `foo-configmap` or `foo-secret`are updated.
|
||||
@@ -50,8 +49,7 @@ metadata:
|
||||
annotations:
|
||||
configmap.reloader.stakater.com/reload: "foo-configmap"
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
template: metadata:
|
||||
```
|
||||
|
||||
Use comma separated list to define multiple configmaps.
|
||||
@@ -62,15 +60,14 @@ metadata:
|
||||
annotations:
|
||||
configmap.reloader.stakater.com/reload: "foo-configmap,bar-configmap,baz-configmap"
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
template: metadata:
|
||||
```
|
||||
|
||||
### Secret
|
||||
|
||||
To perform rolling upgrade when change happens only on specific secrets use below annotation.
|
||||
|
||||
For a `Deployment` called `foo` have a `Secret` called `foo-secret`. Then add this annotation to main metadata of your `Deployment`
|
||||
For a `Deployment` called `foo` have a `Secret` called `foo-secret`. Then add this annotation to main metadata of your `Deployment`
|
||||
|
||||
```yaml
|
||||
kind: Deployment
|
||||
@@ -78,8 +75,7 @@ metadata:
|
||||
annotations:
|
||||
secret.reloader.stakater.com/reload: "foo-secret"
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
template: metadata:
|
||||
```
|
||||
|
||||
Use comma separated list to define multiple secrets.
|
||||
@@ -90,17 +86,19 @@ metadata:
|
||||
annotations:
|
||||
secret.reloader.stakater.com/reload: "foo-secret,bar-secret,baz-secret"
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
template: metadata:
|
||||
```
|
||||
|
||||
### NOTES
|
||||
|
||||
- Reloader also supports [sealed-secrets](https://github.com/bitnami-labs/sealed-secrets). [Here](docs/Reloader-with-Sealed-Secrets.md) are the steps to use sealed-secrets with reloader.
|
||||
- `reloader.stakater.com/auto: "true"` will only reload the pod, if the configmap or secret is used (as a volume mount or as an env) in `DeploymentConfigs/Deployment/Daemonsets/Statefulsets`
|
||||
- `secret.reloader.stakater.com/reload` or `configmap.reloader.stakater.com/reload` annotation will reload the pod upon changes in specified configmap or secret, irrespective of the usage of configmap or secret.
|
||||
- you may override the auto annotation with the `--auto-annotation` flag
|
||||
- you may override the configmap annotation with the `--configmap-annotation` flag
|
||||
- you may override the secret annotation with the `--secret-annotation` flag
|
||||
- you may want to prevent watching certain namespaces with the `--namespaces-to-ignore` flag
|
||||
- you may want to prevent watching certain resources with the `--resources-to-ignore` flag
|
||||
|
||||
## Deploying to Kubernetes
|
||||
|
||||
@@ -114,24 +112,25 @@ You can apply vanilla manifests by changing `RELEASE-NAME` placeholder provided
|
||||
kubectl apply -f https://raw.githubusercontent.com/stakater/Reloader/master/deployments/kubernetes/reloader.yaml
|
||||
```
|
||||
|
||||
By default Reloader gets deployed in `default` namespace and watches changes `secrets` and `configmaps` in all namespaces.
|
||||
By default Reloader gets deployed in `default` namespace and watches changes `secrets` and `configmaps` in all namespaces.
|
||||
|
||||
Reloader can be configured to ignore the resources `secrets` and `configmaps` by passing the following args (`spec.template.spec.containers.args`) to its container :
|
||||
|
||||
| Args | Description |
|
||||
|---|---|
|
||||
| Args | Description |
|
||||
| -------------------------------- | -------------------- |
|
||||
| --resources-to-ignore=configMaps | To ignore configMaps |
|
||||
| --resources-to-ignore=secrets | To ignore secrets |
|
||||
|
||||
`Note`: At one time only one of these resource can be ignored, trying to do it will cause error in Reloader. Workaround for ignoring both resources is by scaling down the reloader pods to `0`.
|
||||
| --resources-to-ignore=secrets | To ignore secrets |
|
||||
|
||||
`Note`: At one time only one of these resource can be ignored, trying to do it will cause error in Reloader. Workaround for ignoring both resources is by scaling down the reloader pods to `0`.
|
||||
|
||||
### Vanilla kustomize
|
||||
|
||||
You can also apply the vanilla manifests by running the following command
|
||||
|
||||
```bash
|
||||
kubectl apply -k https://github.com/stakater/Reloader/deployments/kubernetes
|
||||
```
|
||||
|
||||
Similarly to vanilla manifests get deployed in `default` namespace and watches changes `secrets` and `configmaps` in all namespaces.
|
||||
|
||||
### Kustomize
|
||||
@@ -154,7 +153,7 @@ namespace: reloader
|
||||
|
||||
Alternatively if you have configured helm on your cluster, you can add reloader to helm from our public chart repository and deploy it via helm using below mentioned commands
|
||||
|
||||
```bash
|
||||
```bash
|
||||
helm repo add stakater https://stakater.github.io/stakater-charts
|
||||
|
||||
helm repo update
|
||||
@@ -162,7 +161,7 @@ helm repo update
|
||||
helm install stakater/reloader
|
||||
```
|
||||
|
||||
**Note:** By default reloader watches in all namespaces. To watch in single namespace, please run following command. It will install reloader in `test` namespace which will only watch `Deployments`, `Daemonsets` and `Statefulsets` in `test` namespace.
|
||||
**Note:** By default reloader watches in all namespaces. To watch in single namespace, please run following command. It will install reloader in `test` namespace which will only watch `Deployments`, `Daemonsets` and `Statefulsets` in `test` namespace.
|
||||
|
||||
```bash
|
||||
helm install stakater/reloader --set reloader.watchGlobally=false --namespace test
|
||||
@@ -170,21 +169,21 @@ helm install stakater/reloader --set reloader.watchGlobally=false --namespace te
|
||||
|
||||
Reloader can be configured to ignore the resources `secrets` and `configmaps` by using the following parameters of `values.yaml` file:
|
||||
|
||||
| Parameter | Description | Type |
|
||||
|---|---|---|
|
||||
| ignoreSecrets | To ignore secrets. Valid value are either `true` or `false` | boolean |
|
||||
| Parameter | Description | Type |
|
||||
| ---------------- | -------------------------------------------------------------- | ------- |
|
||||
| ignoreSecrets | To ignore secrets. Valid value are either `true` or `false` | boolean |
|
||||
| ignoreConfigMaps | To ignore configMaps. Valid value are either `true` or `false` | boolean |
|
||||
|
||||
`Note`: At one time only one of these resource can be ignored, trying to do it will cause error in helm template compilation.
|
||||
|
||||
|
||||
|
||||
## Help
|
||||
|
||||
### Documentation
|
||||
|
||||
You can find more documentation [here](docs/)
|
||||
|
||||
### Have 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
|
||||
@@ -204,11 +203,11 @@ Please use the [issue tracker](https://github.com/stakater/Reloader/issues) to r
|
||||
|
||||
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
|
||||
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!
|
||||
|
||||
@@ -227,8 +226,8 @@ Apache2 © [Stakater](http://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/
|
||||
[website]: http://stakater.com/
|
||||
[community]: https://github.com/stakater/
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -25,14 +27,11 @@ func NewReloaderCommand() *cobra.Command {
|
||||
cmd.PersistentFlags().StringVar(&options.SecretUpdateOnChangeAnnotation, "secret-annotation", "secret.reloader.stakater.com/reload", "annotation to detect changes in secrets")
|
||||
cmd.PersistentFlags().StringVar(&options.ReloaderAutoAnnotation, "auto-annotation", "reloader.stakater.com/auto", "annotation to detect changes in secrets")
|
||||
cmd.PersistentFlags().StringSlice("resources-to-ignore", []string{}, "list of resources to ignore (valid options 'configMaps' or 'secrets')")
|
||||
|
||||
cmd.PersistentFlags().StringSlice("namespaces-to-ignore", []string{}, "list of namespaces to ignore")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func startReloader(cmd *cobra.Command, args []string) {
|
||||
var ignoreList util.List
|
||||
var err error
|
||||
|
||||
logrus.Info("Starting Reloader")
|
||||
currentNamespace := os.Getenv("KUBERNETES_NAMESPACE")
|
||||
if len(currentNamespace) == 0 {
|
||||
@@ -46,27 +45,22 @@ func startReloader(cmd *cobra.Command, args []string) {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
ignoreList, err = cmd.Flags().GetStringSlice("resources-to-ignore")
|
||||
ignoredResourcesList, err := getIgnoredResourcesList(cmd)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
for _, v := range ignoreList {
|
||||
if v != "configMaps" && v != "secrets" {
|
||||
logrus.Fatalf("'resources-to-ignore' only accepts 'configMaps' or 'secrets', not '%s'", v)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ignoreList) > 1 {
|
||||
logrus.Fatal("'resources-to-ignore' only accepts 'configMaps' or 'secrets', not both")
|
||||
ignoredNamespacesList, err := getIgnoredNamespacesList(cmd)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
for k := range kube.ResourceMap {
|
||||
if ignoreList.Contains(k) {
|
||||
if ignoredResourcesList.Contains(k) {
|
||||
continue
|
||||
}
|
||||
|
||||
c, err := controller.NewController(clientset, k, currentNamespace)
|
||||
c, err := controller.NewController(clientset, k, currentNamespace, ignoredNamespacesList)
|
||||
if err != nil {
|
||||
logrus.Fatalf("%s", err)
|
||||
}
|
||||
@@ -81,3 +75,36 @@ func startReloader(cmd *cobra.Command, args []string) {
|
||||
// Wait forever
|
||||
select {}
|
||||
}
|
||||
|
||||
func getIgnoredNamespacesList(cmd *cobra.Command) (util.List, error) {
|
||||
return getStringSliceFromFlags(cmd, "namespaces-to-ignore")
|
||||
}
|
||||
|
||||
func getStringSliceFromFlags(cmd *cobra.Command, flag string) ([]string, error) {
|
||||
slice, err := cmd.Flags().GetStringSlice(flag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return slice, nil
|
||||
}
|
||||
|
||||
func getIgnoredResourcesList(cmd *cobra.Command) (util.List, error) {
|
||||
|
||||
ignoredResourcesList, err := getStringSliceFromFlags(cmd, "resources-to-ignore")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, v := range ignoredResourcesList {
|
||||
if v != "configMaps" && v != "secrets" {
|
||||
return nil, fmt.Errorf("'resources-to-ignore' only accepts 'configMaps' or 'secrets', not '%s'", v)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ignoredResourcesList) > 1 {
|
||||
return nil, errors.New("'resources-to-ignore' only accepts 'configMaps' or 'secrets', not both")
|
||||
}
|
||||
|
||||
return ignoredResourcesList, nil
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stakater/Reloader/internal/pkg/handler"
|
||||
"github.com/stakater/Reloader/internal/pkg/util"
|
||||
"github.com/stakater/Reloader/pkg/kube"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
@@ -13,24 +14,28 @@ import (
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// Controller for checking events
|
||||
type Controller struct {
|
||||
client kubernetes.Interface
|
||||
indexer cache.Indexer
|
||||
queue workqueue.RateLimitingInterface
|
||||
informer cache.Controller
|
||||
namespace string
|
||||
client kubernetes.Interface
|
||||
indexer cache.Indexer
|
||||
queue workqueue.RateLimitingInterface
|
||||
informer cache.Controller
|
||||
namespace string
|
||||
ignoredNamespaces util.List
|
||||
}
|
||||
|
||||
// NewController for initializing a Controller
|
||||
func NewController(
|
||||
client kubernetes.Interface, resource string, namespace string) (*Controller, error) {
|
||||
client kubernetes.Interface, resource string, namespace string, ignoredNamespaces []string) (*Controller, error) {
|
||||
|
||||
c := Controller{
|
||||
client: client,
|
||||
namespace: namespace,
|
||||
client: client,
|
||||
namespace: namespace,
|
||||
ignoredNamespaces: ignoredNamespaces,
|
||||
}
|
||||
|
||||
queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
|
||||
@@ -49,9 +54,21 @@ func NewController(
|
||||
|
||||
// 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,
|
||||
})
|
||||
if !c.resourceInIgnoredNamespace(obj) {
|
||||
c.queue.Add(handler.ResourceCreatedHandler{
|
||||
Resource: obj,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) resourceInIgnoredNamespace(raw interface{}) bool {
|
||||
switch object := raw.(type) {
|
||||
case *v1.ConfigMap:
|
||||
return c.ignoredNamespaces.Contains(object.ObjectMeta.Namespace)
|
||||
case *v1.Secret:
|
||||
return c.ignoredNamespaces.Contains(object.ObjectMeta.Namespace)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Update function to add an old object and a new object to the queue in case of updating a resource
|
||||
|
||||
@@ -12,6 +12,9 @@ import (
|
||||
"github.com/stakater/Reloader/internal/pkg/testutil"
|
||||
"github.com/stakater/Reloader/internal/pkg/util"
|
||||
"github.com/stakater/Reloader/pkg/kube"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -30,7 +33,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
logrus.Infof("Creating controller")
|
||||
for k := range kube.ResourceMap {
|
||||
c, err := NewController(clients.KubernetesClient, k, namespace)
|
||||
c, err := NewController(clients.KubernetesClient, k, namespace, []string{})
|
||||
if err != nil {
|
||||
logrus.Fatalf("%s", err)
|
||||
}
|
||||
@@ -1076,3 +1079,87 @@ func TestControllerUpdatingSecretShouldUpdateEnvInStatefulSet(t *testing.T) {
|
||||
}
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
|
||||
func TestController_resourceInIgnoredNamespace(t *testing.T) {
|
||||
type fields struct {
|
||||
client kubernetes.Interface
|
||||
indexer cache.Indexer
|
||||
queue workqueue.RateLimitingInterface
|
||||
informer cache.Controller
|
||||
namespace string
|
||||
ignoredNamespaces util.List
|
||||
}
|
||||
type args struct {
|
||||
raw interface{}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "TestConfigMapResourceInIgnoredNamespaceShouldReturnTrue",
|
||||
fields: fields{
|
||||
ignoredNamespaces: util.List{
|
||||
"system",
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
raw: testutil.GetConfigmap("system", "testcm", "test"),
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "TestSecretResourceInIgnoredNamespaceShouldReturnTrue",
|
||||
fields: fields{
|
||||
ignoredNamespaces: util.List{
|
||||
"system",
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
raw: testutil.GetSecret("system", "testsecret", "test"),
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "TestConfigMapResourceInIgnoredNamespaceShouldReturnFalse",
|
||||
fields: fields{
|
||||
ignoredNamespaces: util.List{
|
||||
"system",
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
raw: testutil.GetConfigmap("some-other-namespace", "testcm", "test"),
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "TestConfigMapResourceInIgnoredNamespaceShouldReturnFalse",
|
||||
fields: fields{
|
||||
ignoredNamespaces: util.List{
|
||||
"system",
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
raw: testutil.GetSecret("some-other-namespace", "testsecret", "test"),
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Controller{
|
||||
client: tt.fields.client,
|
||||
indexer: tt.fields.indexer,
|
||||
queue: tt.fields.queue,
|
||||
informer: tt.fields.informer,
|
||||
namespace: tt.fields.namespace,
|
||||
ignoredNamespaces: tt.fields.ignoredNamespaces,
|
||||
}
|
||||
if got := c.resourceInIgnoredNamespace(tt.args.raw); got != tt.want {
|
||||
t.Errorf("Controller.resourceInIgnoredNamespace() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user