mirror of
https://github.com/mariadb-operator/mariadb-operator.git
synced 2025-07-25 01:28:31 +00:00
Removed recovery operations from agent
This commit is contained in:
@ -37,7 +37,6 @@ var (
|
||||
kubernetesAuth bool
|
||||
kubernetesTrustedName string
|
||||
kubernetesTrustedNamespace string
|
||||
recoveryTimeout time.Duration
|
||||
gracefulShutdownTimeout time.Duration
|
||||
)
|
||||
|
||||
@ -56,8 +55,6 @@ func init() {
|
||||
RootCmd.Flags().StringVar(&kubernetesTrustedName, "kubernetes-trusted-name", "", "Trusted Kubernetes ServiceAccount name to be verified")
|
||||
RootCmd.Flags().StringVar(&kubernetesTrustedNamespace, "kubernetes-trusted-namespace", "", "Trusted Kubernetes ServiceAccount "+
|
||||
"namespace to be verified")
|
||||
RootCmd.Flags().DurationVar(&recoveryTimeout, "recovery-timeout", 1*time.Minute, "Timeout to obtain sequence number "+
|
||||
"during the Galera cluster recovery process")
|
||||
RootCmd.Flags().DurationVar(&gracefulShutdownTimeout, "graceful-shutdown-timeout", 5*time.Second, "Timeout to gracefully terminate "+
|
||||
"in-flight requests")
|
||||
}
|
||||
@ -102,7 +99,6 @@ var RootCmd = &cobra.Command{
|
||||
fileManager,
|
||||
state,
|
||||
&handlerLogger,
|
||||
handler.WithRecoveryTimeout(recoveryTimeout),
|
||||
)
|
||||
|
||||
routerOpts := []router.Option{
|
||||
|
@ -1,6 +1,6 @@
|
||||
##@ Build
|
||||
|
||||
DOCKER_ARGS ?=
|
||||
DOCKER_ARGS ?= --load
|
||||
|
||||
.PHONY: build
|
||||
build: ## Build binary.
|
||||
|
@ -171,7 +171,6 @@ func (b *Builder) maxscaleCommand(mxs *mariadbv1alpha1.MaxScale) (*command.Comma
|
||||
|
||||
func (b *Builder) galeraAgentContainer(mariadb *mariadbv1alpha1.MariaDB) (*corev1.Container, error) {
|
||||
galera := ptr.Deref(mariadb.Spec.Galera, mariadbv1alpha1.Galera{})
|
||||
recovery := ptr.Deref(galera.Recovery, mariadbv1alpha1.GaleraRecovery{})
|
||||
agent := galera.Agent
|
||||
|
||||
container, err := b.buildContainer(agent.Image, agent.ImagePullPolicy, &agent.ContainerTemplate)
|
||||
@ -197,9 +196,6 @@ func (b *Builder) galeraAgentContainer(mariadb *mariadbv1alpha1.MariaDB) (*corev
|
||||
if agent.GracefulShutdownTimeout != nil {
|
||||
args = append(args, fmt.Sprintf("--graceful-shutdown-timeout=%s", agent.GracefulShutdownTimeout.Duration))
|
||||
}
|
||||
if recovery.Enabled && recovery.PodRecoveryTimeout != nil {
|
||||
args = append(args, fmt.Sprintf("--recovery-timeout=%s", recovery.PodRecoveryTimeout.Duration))
|
||||
}
|
||||
if ptr.Deref(agent.KubernetesAuth, mariadbv1alpha1.KubernetesAuth{}).Enabled {
|
||||
args = append(args, []string{
|
||||
"--kubernetes-auth",
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
type Client struct {
|
||||
Bootstrap *Bootstrap
|
||||
State *State
|
||||
Recovery *Recovery
|
||||
}
|
||||
|
||||
func NewClient(baseUrl string, opts ...mdbhttp.Option) (*Client, error) {
|
||||
@ -24,7 +23,6 @@ func NewClient(baseUrl string, opts ...mdbhttp.Option) (*Client, error) {
|
||||
return &Client{
|
||||
Bootstrap: NewBootstrap(httpClient),
|
||||
State: NewState(httpClient),
|
||||
Recovery: NewRecovery(httpClient),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -1,46 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/mariadb-operator/mariadb-operator/pkg/galera/recovery"
|
||||
mdbhttp "github.com/mariadb-operator/mariadb-operator/pkg/http"
|
||||
)
|
||||
|
||||
type Recovery struct {
|
||||
client *mdbhttp.Client
|
||||
}
|
||||
|
||||
func NewRecovery(client *mdbhttp.Client) *Recovery {
|
||||
return &Recovery{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Recovery) Enable(ctx context.Context) error {
|
||||
res, err := r.client.Put(ctx, "/api/recovery", nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return handleResponse(res, nil)
|
||||
}
|
||||
|
||||
func (r *Recovery) Start(ctx context.Context) (*recovery.Bootstrap, error) {
|
||||
res, err := r.client.Post(ctx, "/api/recovery", nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var bootstrap recovery.Bootstrap
|
||||
if err := handleResponse(res, &bootstrap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bootstrap, nil
|
||||
}
|
||||
|
||||
func (r *Recovery) Disable(ctx context.Context) error {
|
||||
res, err := r.client.Delete(ctx, "/api/recovery", nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return handleResponse(res, nil)
|
||||
}
|
@ -59,11 +59,6 @@ func (b *Bootstrap) Enable(w http.ResponseWriter, r *http.Request) {
|
||||
defer b.locker.Unlock()
|
||||
b.logger.V(1).Info("enabling bootstrap")
|
||||
|
||||
if err := b.fileManager.DeleteConfigFile(recovery.RecoveryFileName); err != nil && !os.IsNotExist(err) {
|
||||
b.responseWriter.WriteErrorf(w, "error deleting existing recovery config: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := b.setSafeToBootstrap(&bootstrap); err != nil {
|
||||
b.responseWriter.WriteErrorf(w, "error setting safe to bootstrap: %v", err)
|
||||
return
|
||||
|
@ -14,16 +14,14 @@ import (
|
||||
type Handler struct {
|
||||
Bootstrap *Bootstrap
|
||||
State *State
|
||||
Recovery *Recovery
|
||||
Probe *Probe
|
||||
}
|
||||
|
||||
func NewHandler(mariadbKey types.NamespacedName, client ctrlclient.Client, fileManager *filemanager.FileManager,
|
||||
initState *state.State, logger *logr.Logger, recoveryOpts ...RecoveryOption) *Handler {
|
||||
initState *state.State, logger *logr.Logger) *Handler {
|
||||
mux := &sync.RWMutex{}
|
||||
bootstrapLogger := logger.WithName("bootstrap")
|
||||
stateLogger := logger.WithName("state")
|
||||
recoveryLogger := logger.WithName("recovery")
|
||||
probeLogger := logger.WithName("probe")
|
||||
|
||||
bootstrap := NewBootstrap(
|
||||
@ -39,13 +37,6 @@ func NewHandler(mariadbKey types.NamespacedName, client ctrlclient.Client, fileM
|
||||
mux.RLocker(),
|
||||
&stateLogger,
|
||||
)
|
||||
recovery := NewRecover(
|
||||
fileManager,
|
||||
mdbhttp.NewResponseWriter(&recoveryLogger),
|
||||
mux,
|
||||
&recoveryLogger,
|
||||
recoveryOpts...,
|
||||
)
|
||||
probe := NewProbe(
|
||||
mariadbKey,
|
||||
client,
|
||||
@ -56,7 +47,6 @@ func NewHandler(mariadbKey types.NamespacedName, client ctrlclient.Client, fileM
|
||||
return &Handler{
|
||||
Bootstrap: bootstrap,
|
||||
State: state,
|
||||
Recovery: recovery,
|
||||
Probe: probe,
|
||||
}
|
||||
}
|
||||
|
@ -1,141 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/mariadb-operator/mariadb-operator/pkg/galera/errors"
|
||||
"github.com/mariadb-operator/mariadb-operator/pkg/galera/filemanager"
|
||||
"github.com/mariadb-operator/mariadb-operator/pkg/galera/recovery"
|
||||
mdbhttp "github.com/mariadb-operator/mariadb-operator/pkg/http"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
type Recovery struct {
|
||||
fileManager *filemanager.FileManager
|
||||
responseWriter *mdbhttp.ResponseWriter
|
||||
locker sync.Locker
|
||||
logger *logr.Logger
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
type RecoveryOption func(*Recovery)
|
||||
|
||||
func WithRecoveryTimeout(timeout time.Duration) RecoveryOption {
|
||||
return func(r *Recovery) {
|
||||
r.timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
func NewRecover(fileManager *filemanager.FileManager, responseWriter *mdbhttp.ResponseWriter, locker sync.Locker,
|
||||
logger *logr.Logger, opts ...RecoveryOption) *Recovery {
|
||||
recovery := &Recovery{
|
||||
fileManager: fileManager,
|
||||
responseWriter: responseWriter,
|
||||
locker: locker,
|
||||
logger: logger,
|
||||
timeout: 1 * time.Minute,
|
||||
}
|
||||
for _, setOpts := range opts {
|
||||
setOpts(recovery)
|
||||
}
|
||||
return recovery
|
||||
}
|
||||
|
||||
func (r *Recovery) Enable(w http.ResponseWriter, req *http.Request) {
|
||||
r.locker.Lock()
|
||||
defer r.locker.Unlock()
|
||||
r.logger.V(1).Info("enabling recovery")
|
||||
|
||||
if err := r.fileManager.DeleteConfigFile(recovery.BootstrapFileName); err != nil && !os.IsNotExist(err) {
|
||||
r.responseWriter.WriteErrorf(w, "error deleting existing bootstrap config: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.fileManager.DeleteStateFile(recovery.RecoveryLogFileName); err != nil && !os.IsNotExist(err) {
|
||||
r.responseWriter.WriteErrorf(w, "error deleting existing recovery log: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.fileManager.WriteConfigFile(recovery.RecoveryFileName, []byte(recovery.RecoveryFile)); err != nil {
|
||||
r.responseWriter.WriteErrorf(w, "error writing recovery config: %v", err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (r *Recovery) Start(w http.ResponseWriter, req *http.Request) {
|
||||
r.locker.Lock()
|
||||
defer r.locker.Unlock()
|
||||
r.logger.V(1).Info("starting recovery")
|
||||
|
||||
exists, err := r.fileManager.ConfigFileExists(recovery.RecoveryFileName)
|
||||
if err != nil {
|
||||
r.responseWriter.WriteErrorf(w, "error checking recovery config: %v", err)
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
r.responseWriter.Write(w, errors.NewAPIError("recovery config not found"), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
recoveryCtx, cancel := context.WithTimeout(req.Context(), r.timeout)
|
||||
defer cancel()
|
||||
|
||||
bootstrap, err := r.pollUntilRecovered(recoveryCtx)
|
||||
if err != nil {
|
||||
r.responseWriter.WriteErrorf(w, "error recovering galera: %v", err)
|
||||
return
|
||||
}
|
||||
r.responseWriter.WriteOK(w, bootstrap)
|
||||
}
|
||||
|
||||
func (r *Recovery) Disable(w http.ResponseWriter, req *http.Request) {
|
||||
r.locker.Lock()
|
||||
defer r.locker.Unlock()
|
||||
r.logger.V(1).Info("disabling recovery")
|
||||
|
||||
if err := r.fileManager.DeleteConfigFile(recovery.RecoveryFileName); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
r.responseWriter.Write(w, errors.NewAPIError("recovery config not found"), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
r.responseWriter.WriteErrorf(w, "error deleting recovery config: %v", err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (r *Recovery) pollUntilRecovered(ctx context.Context) (*recovery.Bootstrap, error) {
|
||||
var bootstrap *recovery.Bootstrap
|
||||
err := wait.PollUntilContextCancel(ctx, 1*time.Second, true, func(context.Context) (bool, error) {
|
||||
b, err := r.recover()
|
||||
if err != nil {
|
||||
r.logger.Error(err, "error recovering galera from recovery log")
|
||||
return false, nil
|
||||
}
|
||||
bootstrap = b
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bootstrap, nil
|
||||
}
|
||||
|
||||
func (r *Recovery) recover() (*recovery.Bootstrap, error) {
|
||||
bytes, err := r.fileManager.ReadStateFile(recovery.RecoveryLogFileName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading Galera state file: %v", err)
|
||||
}
|
||||
var bootstrap recovery.Bootstrap
|
||||
if err := bootstrap.Unmarshal(bytes); err != nil {
|
||||
return nil, fmt.Errorf("error unmarshaling bootstrap: %v", err)
|
||||
}
|
||||
return &bootstrap, nil
|
||||
}
|
@ -87,11 +87,6 @@ func apiRouter(h *handler.Handler, k8sClient ctrlclient.Client, logger logr.Logg
|
||||
r.Route("/state", func(r chi.Router) {
|
||||
r.Get("/galera", h.State.GetGaleraState)
|
||||
})
|
||||
r.Route("/recovery", func(r chi.Router) {
|
||||
r.Put("/", h.Recovery.Enable)
|
||||
r.Post("/", h.Recovery.Start)
|
||||
r.Delete("/", h.Recovery.Disable)
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
@ -16,14 +16,6 @@ const (
|
||||
BootstrapFileName = "1-bootstrap.cnf"
|
||||
BootstrapFile = `[galera]
|
||||
wsrep_new_cluster="ON"`
|
||||
RecoveryFileName = "2-recovery.cnf"
|
||||
RecoveryLogFileName = "mariadb.err"
|
||||
)
|
||||
|
||||
var (
|
||||
RecoveryFile = fmt.Sprintf(`[galera]
|
||||
log_error=%s
|
||||
wsrep_recover="ON"`, RecoveryLogFileName)
|
||||
)
|
||||
|
||||
type GaleraRecoverer interface {
|
||||
|
Reference in New Issue
Block a user