Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mysql-configmap-history-limit flags #733

Merged
merged 2 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 19 additions & 17 deletions cmd/moco-controller/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,24 @@ var (
)

var config struct {
metricsAddr string
probeAddr string
pprofAddr string
leaderElectionID string
webhookAddr string
certDir string
grpcCertDir string
agentImage string
backupImage string
fluentBitImage string
exporterImage string
pvcSyncAnnotationKeys []string
pvcSyncLabelKeys []string
interval time.Duration
maxConcurrentReconciles int
qps int
zapOpts zap.Options
metricsAddr string
probeAddr string
pprofAddr string
leaderElectionID string
webhookAddr string
certDir string
grpcCertDir string
agentImage string
backupImage string
fluentBitImage string
exporterImage string
pvcSyncAnnotationKeys []string
pvcSyncLabelKeys []string
interval time.Duration
maxConcurrentReconciles int
mySQLConfigMapHistoryLimit int
qps int
zapOpts zap.Options
}

func init() {
Expand Down Expand Up @@ -109,6 +110,7 @@ func init() {
fs.StringSliceVar(&config.pvcSyncLabelKeys, "pvc-sync-label-keys", []string{}, "The keys of labels from MySQLCluster's volumeClaimTemplates to be synced to the PVC")
fs.DurationVar(&config.interval, "check-interval", 1*time.Minute, "Interval of cluster maintenance")
fs.IntVar(&config.maxConcurrentReconciles, "max-concurrent-reconciles", 8, "The maximum number of concurrent reconciles which can be run")
fs.IntVar(&config.mySQLConfigMapHistoryLimit, "mysql-configmap-history-limit", 10, "The maximum number of MySQLConfigMap's history to be kept")
// The default QPS is 20.
// https://github.com/kubernetes-sigs/controller-runtime/blob/a26de2d610c3cf4b2a02688534aaf5a65749c743/pkg/client/config/config.go#L84-L85
fs.IntVar(&config.qps, "apiserver-qps-throttle", 20, "The maximum QPS to the API server.")
Expand Down
25 changes: 13 additions & 12 deletions cmd/moco-controller/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,18 +102,19 @@ func subMain(ns, addr string, port int) error {
defer clusterMgr.StopAll()

if err = (&controllers.MySQLClusterReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("moco-controller"),
AgentImage: config.agentImage,
BackupImage: config.backupImage,
FluentBitImage: config.fluentBitImage,
ExporterImage: config.exporterImage,
SystemNamespace: ns,
PVCSyncAnnotationKeys: config.pvcSyncAnnotationKeys,
PVCSyncLabelKeys: config.pvcSyncLabelKeys,
ClusterManager: clusterMgr,
MaxConcurrentReconciles: config.maxConcurrentReconciles,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("moco-controller"),
AgentImage: config.agentImage,
BackupImage: config.backupImage,
FluentBitImage: config.fluentBitImage,
ExporterImage: config.exporterImage,
SystemNamespace: ns,
PVCSyncAnnotationKeys: config.pvcSyncAnnotationKeys,
PVCSyncLabelKeys: config.pvcSyncLabelKeys,
ClusterManager: clusterMgr,
MaxConcurrentReconciles: config.maxConcurrentReconciles,
MySQLConfigMapHistoryLimit: config.mySQLConfigMapHistoryLimit,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "MySQLCluster")
return err
Expand Down
37 changes: 25 additions & 12 deletions controllers/mysqlcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"hash/fnv"
"os"
"path/filepath"
"sort"
"strings"
"time"

Expand Down Expand Up @@ -136,17 +137,18 @@ func apply[S any, T clientObjectConstraint[S], U any](ctx context.Context, r cli
// MySQLClusterReconciler reconciles a MySQLCluster object
type MySQLClusterReconciler struct {
client.Client
Scheme *runtime.Scheme
Recorder record.EventRecorder
AgentImage string
BackupImage string
FluentBitImage string
ExporterImage string
SystemNamespace string
PVCSyncAnnotationKeys []string
PVCSyncLabelKeys []string
ClusterManager clustering.ClusterManager
MaxConcurrentReconciles int
Scheme *runtime.Scheme
Recorder record.EventRecorder
AgentImage string
BackupImage string
FluentBitImage string
ExporterImage string
SystemNamespace string
PVCSyncAnnotationKeys []string
PVCSyncLabelKeys []string
ClusterManager clustering.ClusterManager
MaxConcurrentReconciles int
MySQLConfigMapHistoryLimit int
}

//+kubebuilder:rbac:groups=moco.cybozu.com,resources=mysqlclusters,verbs=get;list;watch;update;patch
Expand Down Expand Up @@ -506,11 +508,22 @@ func (r *MySQLClusterReconciler) reconcileV1MyCnf(ctx context.Context, req ctrl.
if err := r.List(ctx, cms, client.InNamespace(cluster.Namespace)); err != nil {
return nil, err
}
for _, old := range cms.Items {

// Sort the ConfigMapList by creation timestamp in descending order
sort.Slice(cms.Items, func(i, j int) bool {
return cms.Items[i].CreationTimestamp.Time.After(cms.Items[j].CreationTimestamp.Time)
})

for i, old := range cms.Items {
if i < r.MySQLConfigMapHistoryLimit {
continue
}

if strings.HasPrefix(old.Name, prefix) && old.Name != cmName {
if err := r.Delete(ctx, &old); err != nil {
return nil, fmt.Errorf("failed to delete old my.cnf configmap %s/%s: %w", old.Namespace, old.Name, err)
}
log.Info("deleted old my.cnf configmap", "configMapName", old.Name)
}
}

Expand Down
62 changes: 43 additions & 19 deletions controllers/mysqlcluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,16 @@ var _ = Describe("MySQLCluster reconciler", func() {
clusters: make(map[string]struct{}),
}
mysqlr := &MySQLClusterReconciler{
Client: mgr.GetClient(),
Scheme: scheme,
Recorder: mgr.GetEventRecorderFor("moco-controller"),
SystemNamespace: testMocoSystemNamespace,
ClusterManager: mockMgr,
AgentImage: testAgentImage,
BackupImage: testBackupImage,
FluentBitImage: testFluentBitImage,
ExporterImage: testExporterImage,
Client: mgr.GetClient(),
Scheme: scheme,
Recorder: mgr.GetEventRecorderFor("moco-controller"),
SystemNamespace: testMocoSystemNamespace,
ClusterManager: mockMgr,
AgentImage: testAgentImage,
BackupImage: testBackupImage,
FluentBitImage: testFluentBitImage,
ExporterImage: testExporterImage,
MySQLConfigMapHistoryLimit: 2,
}
err = mysqlr.SetupWithManager(mgr)
Expect(err).ToNot(HaveOccurred())
Expand Down Expand Up @@ -418,19 +419,28 @@ var _ = Describe("MySQLCluster reconciler", func() {
err = k8sClient.Update(ctx, cluster)
Expect(err).NotTo(HaveOccurred())

oldName := cm.Name
Eventually(func() error {
cms := &corev1.ConfigMapList{}
if err := k8sClient.List(ctx, cms, client.InNamespace("test")); err != nil {
return err
}

var mycnfCMs []*corev1.ConfigMap
for i, cm := range cms.Items {
if cm.Name == oldName {
var mycnfCount int
prefix := cluster.PrefixedName() + "."
for _, cm := range cms.Items {
if !strings.HasPrefix(cm.Name, prefix) {
continue
}
if strings.HasPrefix(cm.Name, "moco-test.") {
mycnfCount++
}

if mycnfCount != 2 {
return fmt.Errorf("the number of config maps is not history limits: %d", len(cms.Items))
}

var mycnfCMs []*corev1.ConfigMap
for i, cm := range cms.Items {
if strings.HasPrefix(cm.Name, "moco-test.") && strings.Contains(cm.Data["my.cnf"], "foo = bar") {
mycnfCMs = append(mycnfCMs, &cms.Items[i])
}
}
Expand All @@ -454,19 +464,28 @@ var _ = Describe("MySQLCluster reconciler", func() {
err = k8sClient.Update(ctx, userCM)
Expect(err).NotTo(HaveOccurred())

oldName = cm.Name
Eventually(func() error {
cms := &corev1.ConfigMapList{}
if err := k8sClient.List(ctx, cms, client.InNamespace("test")); err != nil {
return err
}

var mycnfCMs []*corev1.ConfigMap
for i, cm := range cms.Items {
if cm.Name == oldName {
var mycnfCount int
prefix := cluster.PrefixedName() + "."
for _, cm := range cms.Items {
if !strings.HasPrefix(cm.Name, prefix) {
continue
}
if strings.HasPrefix(cm.Name, "moco-test.") {
mycnfCount++
}

if mycnfCount != 2 {
return fmt.Errorf("the number of config maps is not history limits: %d", len(cms.Items))
}

var mycnfCMs []*corev1.ConfigMap
for i, cm := range cms.Items {
if strings.HasPrefix(cm.Name, "moco-test.") && strings.Contains(cm.Data["my.cnf"], "foo = baz") {
mycnfCMs = append(mycnfCMs, &cms.Items[i])
}
}
Expand All @@ -479,6 +498,11 @@ var _ = Describe("MySQLCluster reconciler", func() {
return nil
}).Should(Succeed())

cms := &corev1.ConfigMapList{}
if err := k8sClient.List(ctx, cms, client.InNamespace("test")); err != nil {
panic(err)
}

masa213f marked this conversation as resolved.
Show resolved Hide resolved
Expect(cm.Data["my.cnf"]).To(ContainSubstring("foo = baz"))
})

Expand Down
71 changes: 37 additions & 34 deletions docs/moco-controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,41 @@

```
Flags:
--add_dir_header If true, adds the file directory to the header of the log messages
--agent-image string The image of moco-agent sidecar container
--alsologtostderr log to standard error as well as files (no effect when -logtostderr=true)
--apiserver-qps-throttle int The maximum QPS to the API server. (default 20)
--backup-image string The image of moco-backup container
--cert-dir string webhook certificate directory
--check-interval duration Interval of cluster maintenance (default 1m0s)
--fluent-bit-image string The image of fluent-bit sidecar container
--grpc-cert-dir string gRPC certificate directory (default "/grpc-cert")
--health-probe-addr string Listen address for health probes (default ":8081")
-h, --help help for moco-controller
--leader-election-id string ID for leader election by controller-runtime (default "moco")
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory (no effect when -logtostderr=true)
--log_file string If non-empty, use this log file (no effect when -logtostderr=true)
--log_file_max_size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
--logtostderr log to standard error instead of files (default true)
--max-concurrent-reconciles int The maximum number of concurrent reconciles which can be run (default 8)
--metrics-addr string Listen address for metric endpoint (default ":8080")
--mysqld-exporter-image string The image of mysqld_exporter sidecar container
--one_output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true)
--pprof-addr string Listen address for pprof endpoints. pprof is disabled by default
--skip_headers If true, avoid header prefixes in the log messages
--skip_log_headers If true, avoid headers when opening log files (no effect when -logtostderr=true)
--stderrthreshold severity logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=false) (default 2)
-v, --v Level number for the log level verbosity
--version version for moco-controller
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
--webhook-addr string Listen address for the webhook endpoint (default ":9443")
--zap-devel Development Mode defaults(encoder=consoleEncoder,logLevel=Debug,stackTraceLevel=Warn). Production Mode defaults(encoder=jsonEncoder,logLevel=Info,stackTraceLevel=Error)
--zap-encoder encoder Zap log encoding (one of 'json' or 'console')
--zap-log-level level Zap Level to configure the verbosity of logging. Can be one of 'debug', 'info', 'error', or any integer value > 0 which corresponds to custom debug levels of increasing verbosity
--zap-stacktrace-level level Zap Level at and above which stacktraces are captured (one of 'info', 'error', 'panic').
--zap-time-encoding time-encoding Zap time encoding (one of 'epoch', 'millis', 'nano', 'iso8601', 'rfc3339' or 'rfc3339nano'). Defaults to 'epoch'.
--add_dir_header If true, adds the file directory to the header of the log messages
--agent-image string The image of moco-agent sidecar container (default "ghcr.io/cybozu-go/moco-agent:0.12.1")
--alsologtostderr log to standard error as well as files (no effect when -logtostderr=true)
--apiserver-qps-throttle int The maximum QPS to the API server. (default 20)
--backup-image string The image of moco-backup container (default "ghcr.io/cybozu-go/moco-backup:0.23.2")
--cert-dir string webhook certificate directory
--check-interval duration Interval of cluster maintenance (default 1m0s)
--fluent-bit-image string The image of fluent-bit sidecar container (default "ghcr.io/cybozu-go/moco/fluent-bit:3.0.2.1")
--grpc-cert-dir string gRPC certificate directory (default "/grpc-cert")
--health-probe-addr string Listen address for health probes (default ":8081")
-h, --help help for moco-controller
--leader-election-id string ID for leader election by controller-runtime (default "moco")
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory (no effect when -logtostderr=true)
--log_file string If non-empty, use this log file (no effect when -logtostderr=true)
--log_file_max_size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
--logtostderr log to standard error instead of files (default true)
--max-concurrent-reconciles int The maximum number of concurrent reconciles which can be run (default 8)
--metrics-addr string Listen address for metric endpoint (default ":8080")
--mysql-configmap-history-limit int The maximum number of MySQLConfigMap's history to be kept (default 10)
--mysqld-exporter-image string The image of mysqld_exporter sidecar container (default "ghcr.io/cybozu-go/moco/mysqld_exporter:0.15.1.2")
--one_output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true)
--pprof-addr string Listen address for pprof endpoints. pprof is disabled by default
--pvc-sync-annotation-keys strings The keys of annotations from MySQLCluster's volumeClaimTemplates to be synced to the PVC
--pvc-sync-label-keys strings The keys of labels from MySQLCluster's volumeClaimTemplates to be synced to the PVC
--skip_headers If true, avoid header prefixes in the log messages
--skip_log_headers If true, avoid headers when opening log files (no effect when -logtostderr=true)
--stderrthreshold severity logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=true) (default 2)
-v, --v Level number for the log level verbosity
--version version for moco-controller
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
--webhook-addr string Listen address for the webhook endpoint (default ":9443")
--zap-devel Development Mode defaults(encoder=consoleEncoder,logLevel=Debug,stackTraceLevel=Warn). Production Mode defaults(encoder=jsonEncoder,logLevel=Info,stackTraceLevel=Error)
--zap-encoder encoder Zap log encoding (one of 'json' or 'console')
--zap-log-level level Zap Level to configure the verbosity of logging. Can be one of 'debug', 'info', 'error', or any integer value > 0 which corresponds to custom debug levels of increasing verbosity
--zap-stacktrace-level level Zap Level at and above which stacktraces are captured (one of 'info', 'error', 'panic').
--zap-time-encoding time-encoding Zap time encoding (one of 'epoch', 'millis', 'nano', 'iso8601', 'rfc3339' or 'rfc3339nano'). Defaults to 'epoch'.
```
Loading