Skip to content

Commit 2260d72

Browse files
committed
feature reload-on-delete implemented, test cases enhanced
1 parent 8595b4a commit 2260d72

File tree

12 files changed

+568
-189
lines changed

12 files changed

+568
-189
lines changed

README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}}
317317
| `reloader.ignoreSecrets` | To ignore secrets. Valid value are either `true` or `false`. Either `ignoreSecrets` or `ignoreConfigMaps` can be ignored, not both at the same time | boolean | `false` |
318318
| `reloader.ignoreConfigMaps` | To ignore configMaps. Valid value are either `true` or `false` | boolean | `false` |
319319
| `reloader.reloadOnCreate` | Enable reload on create events. Valid value are either `true` or `false` | boolean | `false` |
320+
| `reloader.reloadOnDelete` | Enable reload on delete events. Valid value are either `true` or `false` | boolean | `false` |
320321
| `reloader.syncAfterRestart` | Enable sync after Reloader restarts for **Add** events, works only when reloadOnCreate is `true`. Valid value are either `true` or `false` | boolean | `false` |
321322
| `reloader.reloadStrategy` | Strategy to trigger resource restart, set to either `default`, `env-vars` or `annotations` | enumeration | `default` |
322323
| `reloader.ignoreNamespaces` | List of comma separated namespaces to ignore, if multiple are provided, they are combined with the AND operator | string | `""` |
@@ -377,11 +378,15 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}}
377378
1. Configmaps/secrets being added to the cache will cause Reloader to perform a rolling update of the associated workload
378379
1. When applications are deployed for the first time, Reloader will perform a rolling update of the associated workload
379380
1. If you are running Reloader in HA mode all workloads will have a rolling update performed when a new leader is elected
381+
- `reloadOnDelete` controls how Reloader handles secrets being deleted. If `reloadOnDelete` is set to true:
382+
1. Configmaps/secrets being deleted will cause Reloader to perform a rolling update of the associated workload
380383
- `serviceMonitor` will be removed in future releases of Reloader in favour of Pod monitor
381384
- If `reloadOnCreate` is set to false:
382385
1. Updates to configmaps/secrets that occur while there is no leader will not be picked up by the new leader until a subsequent update of the configmap/secret occurs
383386
1. In the worst case the window in which there can be no leader is 15s as this is the LeaseDuration
384-
- By default, `reloadOnCreate` and `syncAfterRestart` are both set to false. Both need to be enabled explicitly
387+
- If `reloadOnDelete` is set to false:
388+
1. Deleting of configmaps/secrets has no effect to pods that references these resources.
389+
- By default, `reloadOnCreate`, `reloadOnDelete` and `syncAfterRestart` are all set to false. All need to be enabled explicitly
385390

386391
## Help
387392

deployments/kubernetes/chart/reloader/templates/deployment.yaml

+4-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ spec:
171171
{{- . | toYaml | nindent 10 }}
172172
{{- end }}
173173
{{- end }}
174-
{{- if or (.Values.reloader.logFormat) (.Values.reloader.ignoreSecrets) (.Values.reloader.ignoreNamespaces) (.Values.reloader.namespaceSelector) (.Values.reloader.resourceLabelSelector) (.Values.reloader.ignoreConfigMaps) (.Values.reloader.custom_annotations) (eq .Values.reloader.isArgoRollouts true) (eq .Values.reloader.reloadOnCreate true) (ne .Values.reloader.reloadStrategy "default") (.Values.reloader.enableHA) (.Values.reloader.autoReloadAll)}}
174+
{{- if or (.Values.reloader.logFormat) (.Values.reloader.ignoreSecrets) (.Values.reloader.ignoreNamespaces) (.Values.reloader.namespaceSelector) (.Values.reloader.resourceLabelSelector) (.Values.reloader.ignoreConfigMaps) (.Values.reloader.custom_annotations) (eq .Values.reloader.isArgoRollouts true) (eq .Values.reloader.reloadOnCreate true) (eq .Values.reloader.reloadOnDelete true) (ne .Values.reloader.reloadStrategy "default") (.Values.reloader.enableHA) (.Values.reloader.autoReloadAll)}}
175175
args:
176176
{{- if .Values.reloader.logFormat }}
177177
- "--log-format={{ .Values.reloader.logFormat }}"
@@ -231,6 +231,9 @@ spec:
231231
{{- if eq .Values.reloader.reloadOnCreate true }}
232232
- "--reload-on-create={{ .Values.reloader.reloadOnCreate }}"
233233
{{- end }}
234+
{{- if eq .Values.reloader.reloadOnDelete true }}
235+
- "--reload-on-delete={{ .Values.reloader.reloadOnDelete }}"
236+
{{- end }}
234237
{{- if eq .Values.reloader.syncAfterRestart true }}
235238
- "--sync-after-restart={{ .Values.reloader.syncAfterRestart }}"
236239
{{- end }}

deployments/kubernetes/chart/reloader/values.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ reloader:
1818
ignoreSecrets: false
1919
ignoreConfigMaps: false
2020
reloadOnCreate: false
21+
reloadOnDelete: false
2122
syncAfterRestart: false
2223
reloadStrategy: default # Set to default, env-vars or annotations
2324
ignoreNamespaces: "" # Comma separated list of namespaces to ignore

internal/pkg/cmd/reloader.go

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func NewReloaderCommand() *cobra.Command {
5151
cmd.PersistentFlags().StringVar(&options.IsArgoRollouts, "is-Argo-Rollouts", "false", "Add support for argo rollouts")
5252
cmd.PersistentFlags().StringVar(&options.ReloadStrategy, constants.ReloadStrategyFlag, constants.EnvVarsReloadStrategy, "Specifies the desired reload strategy")
5353
cmd.PersistentFlags().StringVar(&options.ReloadOnCreate, "reload-on-create", "false", "Add support to watch create events")
54+
cmd.PersistentFlags().StringVar(&options.ReloadOnDelete, "reload-on-delete", "false", "Add support to watch delete events")
5455
cmd.PersistentFlags().BoolVar(&options.EnableHA, "enable-ha", false, "Adds support for running multiple replicas via leadership election")
5556
cmd.PersistentFlags().BoolVar(&options.SyncAfterRestart, "sync-after-restart", false, "Sync add events after reloader restarts")
5657

internal/pkg/controller/controller.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,22 @@ func (c *Controller) Update(old interface{}, new interface{}) {
178178

179179
// Delete function to add an object to the queue in case of deleting a resource
180180
func (c *Controller) Delete(old interface{}) {
181+
182+
if options.ReloadOnDelete == "true" {
183+
if !c.resourceInIgnoredNamespace(old) && c.resourceInSelectedNamespaces(old) && secretControllerInitialized && configmapControllerInitialized {
184+
c.queue.Add(handler.ResourceDeleteHandler{
185+
Resource: old,
186+
Collectors: c.collectors,
187+
Recorder: c.recorder,
188+
})
189+
}
190+
}
191+
181192
switch object := old.(type) {
182193
case *v1.Namespace:
183194
c.removeSelectedNamespaceFromCache(*object)
184195
return
185196
}
186-
187-
// Todo: Any future delete event can be handled here
188197
}
189198

190199
// Run function for controller which handles the queue

internal/pkg/handler/create.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func (r ResourceCreatedHandler) Handle() error {
2727
return sendUpgradeWebhook(config, options.WebhookUrl)
2828
}
2929
// process resource based on its type
30-
return doRollingUpgrade(config, r.Collectors, r.Recorder)
30+
return doRollingUpgrade(config, r.Collectors, r.Recorder, invokeReloadStrategy)
3131
}
3232
return nil
3333
}

internal/pkg/handler/delete.go

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package handler
2+
3+
import (
4+
"github.com/sirupsen/logrus"
5+
"github.com/stakater/Reloader/internal/pkg/callbacks"
6+
"github.com/stakater/Reloader/internal/pkg/constants"
7+
"github.com/stakater/Reloader/internal/pkg/metrics"
8+
"github.com/stakater/Reloader/internal/pkg/options"
9+
"github.com/stakater/Reloader/internal/pkg/testutil"
10+
"github.com/stakater/Reloader/internal/pkg/util"
11+
v1 "k8s.io/api/core/v1"
12+
"k8s.io/apimachinery/pkg/runtime"
13+
"k8s.io/client-go/tools/record"
14+
)
15+
16+
// ResourceDeleteHandler contains new objects
17+
type ResourceDeleteHandler struct {
18+
Resource interface{}
19+
Collectors metrics.Collectors
20+
Recorder record.EventRecorder
21+
}
22+
23+
// Handle processes resources being deleted
24+
func (r ResourceDeleteHandler) Handle() error {
25+
if r.Resource == nil {
26+
logrus.Errorf("Resource delete handler received nil resource")
27+
} else {
28+
config, _ := r.GetConfig()
29+
// Send webhook
30+
if options.WebhookUrl != "" {
31+
return sendUpgradeWebhook(config, options.WebhookUrl)
32+
}
33+
// process resource based on its type
34+
return doRollingUpgrade(config, r.Collectors, r.Recorder, invokeDeleteStrategy)
35+
}
36+
return nil
37+
}
38+
39+
// GetConfig gets configurations containing SHA, annotations, namespace and resource name
40+
func (r ResourceDeleteHandler) GetConfig() (util.Config, string) {
41+
var oldSHAData string
42+
var config util.Config
43+
if _, ok := r.Resource.(*v1.ConfigMap); ok {
44+
config = util.GetConfigmapConfig(r.Resource.(*v1.ConfigMap))
45+
} else if _, ok := r.Resource.(*v1.Secret); ok {
46+
config = util.GetSecretConfig(r.Resource.(*v1.Secret))
47+
} else {
48+
logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource)
49+
}
50+
return config, oldSHAData
51+
}
52+
53+
func invokeDeleteStrategy(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config util.Config, autoReload bool) constants.Result {
54+
if options.ReloadStrategy == constants.AnnotationsReloadStrategy {
55+
return removePodAnnotations(upgradeFuncs, item, config, autoReload)
56+
}
57+
58+
return removeContainerEnvVars(upgradeFuncs, item, config, autoReload)
59+
}
60+
61+
func removePodAnnotations(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config util.Config, autoReload bool) constants.Result {
62+
config.SHAValue = testutil.GetSHAfromEmptyData()
63+
return updatePodAnnotations(upgradeFuncs, item, config, autoReload)
64+
}
65+
66+
func removeContainerEnvVars(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config util.Config, autoReload bool) constants.Result {
67+
envVar := getEnvVarName(config.ResourceName, config.Type)
68+
container := getContainerUsingResource(upgradeFuncs, item, config, autoReload)
69+
70+
if container == nil {
71+
return constants.NoContainerFound
72+
}
73+
74+
//remove if env var exists
75+
containers := upgradeFuncs.ContainersFunc(item)
76+
for i := range containers {
77+
envs := containers[i].Env
78+
index := -1
79+
for j := range envs {
80+
if envs[j].Name == envVar {
81+
index = j
82+
break
83+
}
84+
}
85+
if index != -1 {
86+
containers[i].Env = append(containers[i].Env[:index], containers[i].Env[index+1:]...)
87+
return constants.Updated
88+
}
89+
}
90+
91+
return constants.NotUpdated
92+
}

internal/pkg/handler/update.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func (r ResourceUpdatedHandler) Handle() error {
2929
return sendUpgradeWebhook(config, options.WebhookUrl)
3030
}
3131
// process resource based on its type
32-
return doRollingUpgrade(config, r.Collectors, r.Recorder)
32+
return doRollingUpgrade(config, r.Collectors, r.Recorder, invokeReloadStrategy)
3333
}
3434
}
3535
return nil

internal/pkg/handler/upgrade.go

+29-19
Original file line numberDiff line numberDiff line change
@@ -142,35 +142,35 @@ func sendWebhook(url string) (string, []error) {
142142
return buffer.String(), nil
143143
}
144144

145-
func doRollingUpgrade(config util.Config, collectors metrics.Collectors, recorder record.EventRecorder) error {
145+
func doRollingUpgrade(config util.Config, collectors metrics.Collectors, recorder record.EventRecorder, invoke invokeStrategy) error {
146146
clients := kube.GetClients()
147147

148-
err := rollingUpgrade(clients, config, GetDeploymentRollingUpgradeFuncs(), collectors, recorder)
148+
err := rollingUpgrade(clients, config, GetDeploymentRollingUpgradeFuncs(), collectors, recorder, invoke)
149149
if err != nil {
150150
return err
151151
}
152-
err = rollingUpgrade(clients, config, GetCronJobCreateJobFuncs(), collectors, recorder)
152+
err = rollingUpgrade(clients, config, GetCronJobCreateJobFuncs(), collectors, recorder, invoke)
153153
if err != nil {
154154
return err
155155
}
156-
err = rollingUpgrade(clients, config, GetDaemonSetRollingUpgradeFuncs(), collectors, recorder)
156+
err = rollingUpgrade(clients, config, GetDaemonSetRollingUpgradeFuncs(), collectors, recorder, invoke)
157157
if err != nil {
158158
return err
159159
}
160-
err = rollingUpgrade(clients, config, GetStatefulSetRollingUpgradeFuncs(), collectors, recorder)
160+
err = rollingUpgrade(clients, config, GetStatefulSetRollingUpgradeFuncs(), collectors, recorder, invoke)
161161
if err != nil {
162162
return err
163163
}
164164

165165
if kube.IsOpenshift {
166-
err = rollingUpgrade(clients, config, GetDeploymentConfigRollingUpgradeFuncs(), collectors, recorder)
166+
err = rollingUpgrade(clients, config, GetDeploymentConfigRollingUpgradeFuncs(), collectors, recorder, invoke)
167167
if err != nil {
168168
return err
169169
}
170170
}
171171

172172
if options.IsArgoRollouts == "true" {
173-
err = rollingUpgrade(clients, config, GetArgoRolloutRollingUpgradeFuncs(), collectors, recorder)
173+
err = rollingUpgrade(clients, config, GetArgoRolloutRollingUpgradeFuncs(), collectors, recorder, invoke)
174174
if err != nil {
175175
return err
176176
}
@@ -179,17 +179,17 @@ func doRollingUpgrade(config util.Config, collectors metrics.Collectors, recorde
179179
return nil
180180
}
181181

182-
func rollingUpgrade(clients kube.Clients, config util.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, recorder record.EventRecorder) error {
182+
func rollingUpgrade(clients kube.Clients, config util.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, recorder record.EventRecorder, strategy invokeStrategy) error {
183183

184-
err := PerformRollingUpgrade(clients, config, upgradeFuncs, collectors, recorder)
184+
err := PerformAction(clients, config, upgradeFuncs, collectors, recorder, strategy)
185185
if err != nil {
186186
logrus.Errorf("Rolling upgrade for '%s' failed with error = %v", config.ResourceName, err)
187187
}
188188
return err
189189
}
190190

191-
// PerformRollingUpgrade upgrades the deployment if there is any change in configmap or secret data
192-
func PerformRollingUpgrade(clients kube.Clients, config util.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, recorder record.EventRecorder) error {
191+
// PerformAction invokes the deployment if there is any change in configmap or secret data
192+
func PerformAction(clients kube.Clients, config util.Config, upgradeFuncs callbacks.RollingUpgradeFuncs, collectors metrics.Collectors, recorder record.EventRecorder, strategy invokeStrategy) error {
193193
items := upgradeFuncs.ItemsFunc(clients, config.Namespace)
194194

195195
for _, i := range items {
@@ -210,7 +210,7 @@ func PerformRollingUpgrade(clients kube.Clients, config util.Config, upgradeFunc
210210
reloaderEnabled, _ := strconv.ParseBool(reloaderEnabledValue)
211211
typedAutoAnnotationEnabled, _ := strconv.ParseBool(typedAutoAnnotationEnabledValue)
212212
if reloaderEnabled || typedAutoAnnotationEnabled || reloaderEnabledValue == "" && typedAutoAnnotationEnabledValue == "" && options.AutoReloadAll {
213-
result = invokeReloadStrategy(upgradeFuncs, i, config, true)
213+
result = strategy(upgradeFuncs, i, config, true)
214214
}
215215

216216
if result != constants.Updated && annotationValue != "" {
@@ -219,7 +219,7 @@ func PerformRollingUpgrade(clients kube.Clients, config util.Config, upgradeFunc
219219
value = strings.TrimSpace(value)
220220
re := regexp.MustCompile("^" + value + "$")
221221
if re.Match([]byte(config.ResourceName)) {
222-
result = invokeReloadStrategy(upgradeFuncs, i, config, false)
222+
result = strategy(upgradeFuncs, i, config, false)
223223
if result == constants.Updated {
224224
break
225225
}
@@ -230,7 +230,7 @@ func PerformRollingUpgrade(clients kube.Clients, config util.Config, upgradeFunc
230230
if result != constants.Updated && searchAnnotationValue == "true" {
231231
matchAnnotationValue := config.ResourceAnnotations[options.SearchMatchAnnotation]
232232
if matchAnnotationValue == "true" {
233-
result = invokeReloadStrategy(upgradeFuncs, i, config, true)
233+
result = strategy(upgradeFuncs, i, config, true)
234234
}
235235
}
236236

@@ -380,6 +380,8 @@ func getContainerUsingResource(upgradeFuncs callbacks.RollingUpgradeFuncs, item
380380
return container
381381
}
382382

383+
type invokeStrategy func(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config util.Config, autoReload bool) constants.Result
384+
383385
func invokeReloadStrategy(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config util.Config, autoReload bool) constants.Result {
384386
if options.ReloadStrategy == constants.AnnotationsReloadStrategy {
385387
return updatePodAnnotations(upgradeFuncs, item, config, autoReload)
@@ -416,6 +418,13 @@ func updatePodAnnotations(upgradeFuncs callbacks.RollingUpgradeFuncs, item runti
416418
return constants.Updated
417419
}
418420

421+
func getReloaderAnnotationKey() string {
422+
return fmt.Sprintf("%s/%s",
423+
constants.ReloaderAnnotationPrefix,
424+
constants.LastReloadedFromAnnotation,
425+
)
426+
}
427+
419428
func createReloadedAnnotations(target *util.ReloadSource) (map[string]string, error) {
420429
if target == nil {
421430
return nil, errors.New("target is required")
@@ -426,10 +435,7 @@ func createReloadedAnnotations(target *util.ReloadSource) (map[string]string, er
426435
// Intentionally only storing the last item in order to keep
427436
// the generated annotations as small as possible.
428437
annotations := make(map[string]string)
429-
lastReloadedResourceName := fmt.Sprintf("%s/%s",
430-
constants.ReloaderAnnotationPrefix,
431-
constants.LastReloadedFromAnnotation,
432-
)
438+
lastReloadedResourceName := getReloaderAnnotationKey()
433439

434440
lastReloadedResource, err := json.Marshal(target)
435441
if err != nil {
@@ -440,9 +446,13 @@ func createReloadedAnnotations(target *util.ReloadSource) (map[string]string, er
440446
return annotations, nil
441447
}
442448

449+
func getEnvVarName(resourceName string, typeName string) string {
450+
return constants.EnvVarPrefix + util.ConvertToEnvVarName(resourceName) + "_" + typeName
451+
}
452+
443453
func updateContainerEnvVars(upgradeFuncs callbacks.RollingUpgradeFuncs, item runtime.Object, config util.Config, autoReload bool) constants.Result {
444454
var result constants.Result
445-
envVar := constants.EnvVarPrefix + util.ConvertToEnvVarName(config.ResourceName) + "_" + config.Type
455+
envVar := getEnvVarName(config.ResourceName, config.Type)
446456
container := getContainerUsingResource(upgradeFuncs, item, config, autoReload)
447457

448458
if container == nil {

0 commit comments

Comments
 (0)