-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #136 from arangodb/feature/pod-finalizers
Feature: finalizers
- Loading branch information
Showing
31 changed files
with
1,184 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Lifecycle hooks & Finalizers | ||
|
||
The ArangoDB operator expects full control of the `Pods` and `PersistentVolumeClaims` it creates. | ||
Therefore it takes measures to prevent the removal of those resources | ||
until it is safe to do so. | ||
|
||
To achieve this, the server containers in the `Pods` have | ||
a `preStop` hook configured and finalizers are added to the `Pods` | ||
and `PersistentVolumeClaims`. | ||
|
||
The `preStop` hook executes a binary that waits until all finalizers of | ||
the current pod have been removed. | ||
Until this `preStop` hook terminates, Kubernetes will not send a `TERM` signal | ||
to the processes inside the container, which ensures that the server remains running | ||
until it is safe to stop them. | ||
|
||
The operator performs all actions needed when a delete of a `Pod` or | ||
`PersistentVolumeClaims` has been triggered. | ||
E.g. for a dbserver it cleans out the server if the `Pod` and `PersistentVolumeClaim` are being deleted. | ||
|
||
## Lifecycle init-container | ||
|
||
Because the binary that is called in the `preStop` hook is not part of a standard | ||
ArangoDB docker image, it has to be brought into the filesystem of a `Pod`. | ||
This is done by an initial container that copies the binary to an `emptyDir` volume that | ||
is shared between the init-container and the server container. | ||
|
||
## Finalizers | ||
|
||
The ArangoDB operators adds the following finalizers to `Pods`. | ||
|
||
- `dbserver.database.arangodb.com/drain`: Added to DBServers, removed only when the dbserver can be restarted or is completely drained | ||
- `agent.database.arangodb.com/agency-serving`: Added to Agents, removed only when enough agents are left to keep the agency serving | ||
|
||
The ArangoDB operators adds the following finalizers to `PersistentVolumeClaims`. | ||
|
||
- `pvc.database.arangodb.com/member-exists`: removed only when its member exists no longer exists or can be safely rebuild |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
// | ||
// DISCLAIMER | ||
// | ||
// Copyright 2018 ArangoDB GmbH, Cologne, Germany | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// Copyright holder is ArangoDB GmbH, Cologne, Germany | ||
// | ||
// Author Ewout Prangsma | ||
// | ||
|
||
package main | ||
|
||
import ( | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"time" | ||
|
||
"github.com/spf13/cobra" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
||
"github.com/arangodb/kube-arangodb/pkg/util/constants" | ||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil" | ||
) | ||
|
||
var ( | ||
cmdLifecycle = &cobra.Command{ | ||
Use: "lifecycle", | ||
Run: cmdUsage, | ||
Hidden: true, | ||
} | ||
|
||
cmdLifecyclePreStop = &cobra.Command{ | ||
Use: "preStop", | ||
Run: cmdLifecyclePreStopRun, | ||
Hidden: true, | ||
} | ||
cmdLifecycleCopy = &cobra.Command{ | ||
Use: "copy", | ||
Run: cmdLifecycleCopyRun, | ||
Hidden: true, | ||
} | ||
|
||
lifecycleCopyOptions struct { | ||
TargetDir string | ||
} | ||
) | ||
|
||
func init() { | ||
cmdMain.AddCommand(cmdLifecycle) | ||
cmdLifecycle.AddCommand(cmdLifecyclePreStop) | ||
cmdLifecycle.AddCommand(cmdLifecycleCopy) | ||
|
||
cmdLifecycleCopy.Flags().StringVar(&lifecycleCopyOptions.TargetDir, "target", "", "Target directory to copy the executable to") | ||
} | ||
|
||
// Wait until all finalizers of the current pod have been removed. | ||
func cmdLifecyclePreStopRun(cmd *cobra.Command, args []string) { | ||
cliLog.Info().Msgf("Starting arangodb-operator, lifecycle preStop, version %s build %s", projectVersion, projectBuild) | ||
|
||
// Get environment | ||
namespace := os.Getenv(constants.EnvOperatorPodNamespace) | ||
if len(namespace) == 0 { | ||
cliLog.Fatal().Msgf("%s environment variable missing", constants.EnvOperatorPodNamespace) | ||
} | ||
name := os.Getenv(constants.EnvOperatorPodName) | ||
if len(name) == 0 { | ||
cliLog.Fatal().Msgf("%s environment variable missing", constants.EnvOperatorPodName) | ||
} | ||
|
||
// Create kubernetes client | ||
kubecli, err := k8sutil.NewKubeClient() | ||
if err != nil { | ||
cliLog.Fatal().Err(err).Msg("Failed to create Kubernetes client") | ||
} | ||
|
||
pods := kubecli.CoreV1().Pods(namespace) | ||
recentErrors := 0 | ||
for { | ||
p, err := pods.Get(name, metav1.GetOptions{}) | ||
if k8sutil.IsNotFound(err) { | ||
cliLog.Warn().Msg("Pod not found") | ||
return | ||
} else if err != nil { | ||
recentErrors++ | ||
cliLog.Error().Err(err).Msg("Failed to get pod") | ||
if recentErrors > 20 { | ||
cliLog.Fatal().Err(err).Msg("Too many recent errors") | ||
return | ||
} | ||
} else { | ||
// We got our pod | ||
finalizerCount := len(p.GetFinalizers()) | ||
if finalizerCount == 0 { | ||
// No more finalizers, we're done | ||
cliLog.Info().Msg("All finalizers gone, we can stop now") | ||
return | ||
} | ||
cliLog.Info().Msgf("Waiting for %d more finalizers to be removed", finalizerCount) | ||
} | ||
// Wait a bit | ||
time.Sleep(time.Second) | ||
} | ||
} | ||
|
||
// Copy the executable to a given place. | ||
func cmdLifecycleCopyRun(cmd *cobra.Command, args []string) { | ||
cliLog.Info().Msgf("Starting arangodb-operator, lifecycle copy, version %s build %s", projectVersion, projectBuild) | ||
|
||
exePath, err := os.Executable() | ||
if err != nil { | ||
cliLog.Fatal().Err(err).Msg("Failed to get executable path") | ||
} | ||
|
||
// Open source | ||
rd, err := os.Open(exePath) | ||
if err != nil { | ||
cliLog.Fatal().Err(err).Msg("Failed to open executable file") | ||
} | ||
defer rd.Close() | ||
|
||
// Open target | ||
targetPath := filepath.Join(lifecycleCopyOptions.TargetDir, filepath.Base(exePath)) | ||
wr, err := os.Create(targetPath) | ||
if err != nil { | ||
cliLog.Fatal().Err(err).Msg("Failed to create target file") | ||
} | ||
defer wr.Close() | ||
|
||
if _, err := io.Copy(wr, rd); err != nil { | ||
cliLog.Fatal().Err(err).Msg("Failed to copy") | ||
} | ||
|
||
// Set file mode | ||
if err := os.Chmod(targetPath, 0755); err != nil { | ||
cliLog.Fatal().Err(err).Msg("Failed to chmod") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// | ||
// DISCLAIMER | ||
// | ||
// Copyright 2018 ArangoDB GmbH, Cologne, Germany | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// Copyright holder is ArangoDB GmbH, Cologne, Germany | ||
// | ||
// Author Ewout Prangsma | ||
// | ||
|
||
package deployment | ||
|
||
import ( | ||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil" | ||
) | ||
|
||
// removePodFinalizers removes all finalizers from all pods owned by us. | ||
func (d *Deployment) removePodFinalizers() error { | ||
log := d.deps.Log | ||
kubecli := d.GetKubeCli() | ||
pods, err := d.GetOwnedPods() | ||
if err != nil { | ||
return maskAny(err) | ||
} | ||
for _, p := range pods { | ||
if err := k8sutil.RemovePodFinalizers(log, kubecli, &p, p.GetFinalizers()); err != nil { | ||
log.Warn().Err(err).Msg("Failed to remove pod finalizers") | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// removePVCFinalizers removes all finalizers from all PVCs owned by us. | ||
func (d *Deployment) removePVCFinalizers() error { | ||
log := d.deps.Log | ||
kubecli := d.GetKubeCli() | ||
pvcs, err := d.GetOwnedPVCs() | ||
if err != nil { | ||
return maskAny(err) | ||
} | ||
for _, p := range pvcs { | ||
if err := k8sutil.RemovePVCFinalizers(log, kubecli, &p, p.GetFinalizers()); err != nil { | ||
log.Warn().Err(err).Msg("Failed to remove PVC finalizers") | ||
} | ||
} | ||
return nil | ||
} |
Oops, something went wrong.