From b2a07f32d2b9bd00a832a9099243a9b17e608f2f Mon Sep 17 00:00:00 2001 From: Balazs Gibizer Date: Wed, 21 Feb 2024 21:09:49 +0100 Subject: [PATCH] Create DB purge CronJob --- api/go.mod | 14 +-- api/go.sum | 28 +++--- config/rbac/role.yaml | 12 +++ controllers/novaconductor_controller.go | 41 ++++++++ go.mod | 18 ++-- go.sum | 36 +++---- pkg/novaconductor/dbpurge.go | 93 +++++++++++++++++++ templates/novaconductor/bin/dbpurge.sh | 23 +++++ .../config/nova-conductor-dbpurge-config.json | 37 ++++++++ test/functional/base_test.go | 16 ++++ test/functional/nova_reconfiguration_test.go | 23 +++++ .../novaconductor_controller_test.go | 25 +++++ 12 files changed, 318 insertions(+), 48 deletions(-) create mode 100644 pkg/novaconductor/dbpurge.go create mode 100755 templates/novaconductor/bin/dbpurge.sh create mode 100644 templates/novaconductor/config/nova-conductor-dbpurge-config.json diff --git a/api/go.mod b/api/go.mod index 61d9f3462..dd2419eb9 100644 --- a/api/go.mod +++ b/api/go.mod @@ -3,11 +3,11 @@ module github.com/openstack-k8s-operators/nova-operator/api go 1.20 require ( - github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240216173409-86913e6d5885 - k8s.io/api v0.28.3 - k8s.io/apimachinery v0.28.3 + github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240224182407-3b6c02b195f6 + k8s.io/api v0.28.7 + k8s.io/apimachinery v0.28.7 k8s.io/utils v0.0.0-20240102154912-e7106e64919e - sigs.k8s.io/controller-runtime v0.16.4 + sigs.k8s.io/controller-runtime v0.16.5 ) require ( @@ -55,9 +55,9 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.28.3 // indirect - k8s.io/client-go v0.28.3 // indirect - k8s.io/component-base v0.28.3 // indirect + k8s.io/apiextensions-apiserver v0.28.7 // indirect + k8s.io/client-go v0.28.7 // indirect + k8s.io/component-base v0.28.7 // indirect k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect diff --git a/api/go.sum b/api/go.sum index 1883ab33d..b15d20f60 100644 --- a/api/go.sum +++ b/api/go.sum @@ -73,8 +73,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY= github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= -github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240216173409-86913e6d5885 h1:o7KZaxKt8Dr97ZJIBPW0P482gLyFEURKF89fizcJCBQ= -github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240216173409-86913e6d5885/go.mod h1:bQwzyQtWCR9F0+IvWZ30J9d1lB6tcX3CNJ0Ten1smDw= +github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240224182407-3b6c02b195f6 h1:mWYmNGOWZY/d24sqGq3uNw0vdUJon4n/rYkhdVqiGgc= +github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240224182407-3b6c02b195f6/go.mod h1:oB0Lc1U5wiJZBdT3lSMkZfG/LhHR6z/SJ0Tp7Y/GUkg= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -167,24 +167,24 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= -k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= -k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= -k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= -k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= -k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= -k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= -k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= -k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= -k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= +k8s.io/api v0.28.7 h1:YKIhBxjXKaxuxWJnwohV0aGjRA5l4IU0Eywf/q19AVI= +k8s.io/api v0.28.7/go.mod h1:y4RbcjCCMff1930SG/TcP3AUKNfaJUgIeUp58e/2vyY= +k8s.io/apiextensions-apiserver v0.28.7 h1:NQlzP/vmvIO9Qt7wQTdMe9sGWGkozQZMPk9suehAvR8= +k8s.io/apiextensions-apiserver v0.28.7/go.mod h1:ST+ZOppyy+Z0mIxezSOK8qwIXctNwdFLNpGkQp8bw4M= +k8s.io/apimachinery v0.28.7 h1:2Z38/XRAOcpb+PonxmBEmjG7hBfmmr41xnr0XvpTnB4= +k8s.io/apimachinery v0.28.7/go.mod h1:QFNX/kCl/EMT2WTSz8k4WLCv2XnkOLMaL8GAVRMdpsA= +k8s.io/client-go v0.28.7 h1:3L6402+tjmOl8twX3fjUQ/wsYAkw6UlVNDVP+rF6YGA= +k8s.io/client-go v0.28.7/go.mod h1:xIoEaDewZ+EwWOo1/F1t0IOKMPe1rwBZhLu9Es6y0tE= +k8s.io/component-base v0.28.7 h1:Cq5aQ52N0CTaOMiary4rXzR4RoTP77Z3ll4qSg4qH7s= +k8s.io/component-base v0.28.7/go.mod h1:RrtNBKrSuckksSQ3fV9PhwBSHO/ZbwJXM2Z0OPx+UJk= k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.16.4 h1:XMh7dF19MlyvMfQCHvH929YGg2WFrIuJ4N5sx3G7U+k= -sigs.k8s.io/controller-runtime v0.16.4/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/controller-runtime v0.16.5 h1:yr1cEJbX08xsTW6XEIzT13KHHmIyX8Umvme2cULvFZw= +sigs.k8s.io/controller-runtime v0.16.5/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 660de7087..1317fa36c 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -39,6 +39,18 @@ rules: - patch - update - watch +- apiGroups: + - batch + resources: + - cronjobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - batch resources: diff --git a/controllers/novaconductor_controller.go b/controllers/novaconductor_controller.go index 2d70bcefe..ab03c2be2 100644 --- a/controllers/novaconductor_controller.go +++ b/controllers/novaconductor_controller.go @@ -37,6 +37,7 @@ import ( "github.com/go-logr/logr" common "github.com/openstack-k8s-operators/lib-common/modules/common" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/cronjob" "github.com/openstack-k8s-operators/lib-common/modules/common/env" helper "github.com/openstack-k8s-operators/lib-common/modules/common/helper" job "github.com/openstack-k8s-operators/lib-common/modules/common/job" @@ -67,6 +68,7 @@ func (r *NovaConductorReconciler) GetLogger(ctx context.Context) logr.Logger { //+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete; //+kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;update;patch;delete; //+kubebuilder:rbac:groups=k8s.cni.cncf.io,resources=network-attachment-definitions,verbs=get;list;watch +// +kubebuilder:rbac:groups=batch,resources=cronjobs,verbs=get;list;watch;create;update;patch;delete; // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -231,6 +233,11 @@ func (r *NovaConductorReconciler) Reconcile(ctx context.Context, req ctrl.Reques return result, err } + err = r.ensureDBPurgeCronJob(ctx, h, instance, serviceAnnotations) + if err != nil { + return ctrl.Result{}, err + } + // clean up nova services from nova db should be always a last step in reconcile err = r.cleanServiceFromNovaDb(ctx, h, instance, secret, Log) if err != nil { @@ -300,6 +307,11 @@ func (r *NovaConductorReconciler) initConditions( condition.InitReason, condition.InputReadyInitMessage, ), + condition.UnknownCondition( + condition.CronJobReadyCondition, + condition.InitReason, + condition.CronJobReadyInitMessage, + ), ) instance.Status.Conditions.Init(&cl) @@ -492,6 +504,34 @@ func (r *NovaConductorReconciler) ensureDeployment( return ctrl.Result{}, nil } +func (r *NovaConductorReconciler) ensureDBPurgeCronJob( + ctx context.Context, + h *helper.Helper, + instance *novav1.NovaConductor, + annotations map[string]string, +) error { + serviceLabels := map[string]string{ + common.AppSelector: NovaConductorLabelPrefix, + } + cronDef := novaconductor.DBPurgeCronJob(instance, serviceLabels, annotations) + cronjob := cronjob.NewCronJob(cronDef, r.RequeueTimeout) + + _, err := cronjob.CreateOrPatch(ctx, h) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.CronJobReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.CronJobReadyErrorMessage, + err.Error())) + return err + } + + instance.Status.Conditions.MarkTrue( + condition.CronJobReadyCondition, condition.CronJobReadyMessage) + return nil +} + func (r *NovaConductorReconciler) cleanServiceFromNovaDb( ctx context.Context, h *helper.Helper, @@ -579,6 +619,7 @@ func (r *NovaConductorReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&novav1.NovaConductor{}). Owns(&v1.StatefulSet{}). Owns(&batchv1.Job{}). + Owns(&batchv1.CronJob{}). Owns(&corev1.Secret{}). // watch the input secrets Watches( diff --git a/go.mod b/go.mod index f2f772c1a..4b6ae65ec 100644 --- a/go.mod +++ b/go.mod @@ -11,19 +11,19 @@ require ( github.com/onsi/gomega v1.30.0 github.com/openstack-k8s-operators/infra-operator/apis v0.3.1-0.20240219072823-a587b364203f github.com/openstack-k8s-operators/keystone-operator/api v0.3.1-0.20240219094943-9bbb46c9afba - github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240216173409-86913e6d5885 - github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20240216173409-86913e6d5885 - github.com/openstack-k8s-operators/lib-common/modules/test v0.3.1-0.20240216173409-86913e6d5885 + github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240224182407-3b6c02b195f6 + github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20240224182407-3b6c02b195f6 + github.com/openstack-k8s-operators/lib-common/modules/test v0.3.1-0.20240224182407-3b6c02b195f6 github.com/openstack-k8s-operators/mariadb-operator/api v0.3.1-0.20240219072536-62f6b4dc7798 github.com/openstack-k8s-operators/nova-operator/api v0.0.0-20221209164002-f9e6b9363961 go.uber.org/zap v1.26.0 golang.org/x/exp v0.0.0-20240213143201-ec583247a57a gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.28.3 - k8s.io/apimachinery v0.28.3 - k8s.io/client-go v0.28.3 + k8s.io/api v0.28.7 + k8s.io/apimachinery v0.28.7 + k8s.io/client-go v0.28.7 k8s.io/utils v0.0.0-20240102154912-e7106e64919e - sigs.k8s.io/controller-runtime v0.16.4 + sigs.k8s.io/controller-runtime v0.16.5 ) require ( @@ -74,8 +74,8 @@ require ( google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiextensions-apiserver v0.28.3 // indirect - k8s.io/component-base v0.28.3 // indirect + k8s.io/apiextensions-apiserver v0.28.7 // indirect + k8s.io/component-base v0.28.7 // indirect k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect diff --git a/go.sum b/go.sum index 9a8406c5b..1a143dd7e 100644 --- a/go.sum +++ b/go.sum @@ -95,12 +95,12 @@ github.com/openstack-k8s-operators/infra-operator/apis v0.3.1-0.20240219072823-a github.com/openstack-k8s-operators/infra-operator/apis v0.3.1-0.20240219072823-a587b364203f/go.mod h1:FGKwlmAIgTsvvz2+uusWqNYFKHNAm4uzVyKao+emeu0= github.com/openstack-k8s-operators/keystone-operator/api v0.3.1-0.20240219094943-9bbb46c9afba h1:E/4DVkBwTxAgea7NRtkStoz66cUXZacZmEJlGcr5/0o= github.com/openstack-k8s-operators/keystone-operator/api v0.3.1-0.20240219094943-9bbb46c9afba/go.mod h1:YyoDWNxCFstwhVRAcEh2X6bXBG0ML5iEhOYQhltgqi4= -github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240216173409-86913e6d5885 h1:o7KZaxKt8Dr97ZJIBPW0P482gLyFEURKF89fizcJCBQ= -github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240216173409-86913e6d5885/go.mod h1:bQwzyQtWCR9F0+IvWZ30J9d1lB6tcX3CNJ0Ten1smDw= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20240216173409-86913e6d5885 h1:g3wUugrt+GsQMtWdxQDzFXjY13U/FKLx1dVMMMJhQqA= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20240216173409-86913e6d5885/go.mod h1:8QsCFttAm+X6A8I8EQThGjNjeMAYt2hK7ivbvnR3434= -github.com/openstack-k8s-operators/lib-common/modules/test v0.3.1-0.20240216173409-86913e6d5885 h1:ioJ2MO3vAcBkLM+0UBu5IuKW/DPXcyiNSOLq0Xvn+Nw= -github.com/openstack-k8s-operators/lib-common/modules/test v0.3.1-0.20240216173409-86913e6d5885/go.mod h1:82nzS+DbBe1tzaMvNHH8FctmZzQ14ZAJysFGsMJiivo= +github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240224182407-3b6c02b195f6 h1:mWYmNGOWZY/d24sqGq3uNw0vdUJon4n/rYkhdVqiGgc= +github.com/openstack-k8s-operators/lib-common/modules/common v0.3.1-0.20240224182407-3b6c02b195f6/go.mod h1:oB0Lc1U5wiJZBdT3lSMkZfG/LhHR6z/SJ0Tp7Y/GUkg= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20240224182407-3b6c02b195f6 h1:u03dfqrO8ml0gShw11Mm+vmqOsSXbY9pv+4sfo2HaTQ= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.3.1-0.20240224182407-3b6c02b195f6/go.mod h1:K1FWWDoDOvMkeyz26x2dNKFJMOad041AXhTL2S8JAlA= +github.com/openstack-k8s-operators/lib-common/modules/test v0.3.1-0.20240224182407-3b6c02b195f6 h1:8SbXBGb7qgvYTXF9WiaNg1esn2J7mVXkqcAC0pIZJe4= +github.com/openstack-k8s-operators/lib-common/modules/test v0.3.1-0.20240224182407-3b6c02b195f6/go.mod h1:82nzS+DbBe1tzaMvNHH8FctmZzQ14ZAJysFGsMJiivo= github.com/openstack-k8s-operators/mariadb-operator/api v0.3.1-0.20240219072536-62f6b4dc7798 h1:zL4DdQ5HPXCLHeRMAWC2zI7ypbkZVYg3UkyEFSnzeow= github.com/openstack-k8s-operators/mariadb-operator/api v0.3.1-0.20240219072536-62f6b4dc7798/go.mod h1:PDqfLbP4ZWqQHAu1OtbjfpOGQUKSzLqRJChvE/9pcyQ= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -225,24 +225,24 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= -k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= -k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= -k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= -k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= -k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= -k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= -k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= -k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= -k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= +k8s.io/api v0.28.7 h1:YKIhBxjXKaxuxWJnwohV0aGjRA5l4IU0Eywf/q19AVI= +k8s.io/api v0.28.7/go.mod h1:y4RbcjCCMff1930SG/TcP3AUKNfaJUgIeUp58e/2vyY= +k8s.io/apiextensions-apiserver v0.28.7 h1:NQlzP/vmvIO9Qt7wQTdMe9sGWGkozQZMPk9suehAvR8= +k8s.io/apiextensions-apiserver v0.28.7/go.mod h1:ST+ZOppyy+Z0mIxezSOK8qwIXctNwdFLNpGkQp8bw4M= +k8s.io/apimachinery v0.28.7 h1:2Z38/XRAOcpb+PonxmBEmjG7hBfmmr41xnr0XvpTnB4= +k8s.io/apimachinery v0.28.7/go.mod h1:QFNX/kCl/EMT2WTSz8k4WLCv2XnkOLMaL8GAVRMdpsA= +k8s.io/client-go v0.28.7 h1:3L6402+tjmOl8twX3fjUQ/wsYAkw6UlVNDVP+rF6YGA= +k8s.io/client-go v0.28.7/go.mod h1:xIoEaDewZ+EwWOo1/F1t0IOKMPe1rwBZhLu9Es6y0tE= +k8s.io/component-base v0.28.7 h1:Cq5aQ52N0CTaOMiary4rXzR4RoTP77Z3ll4qSg4qH7s= +k8s.io/component-base v0.28.7/go.mod h1:RrtNBKrSuckksSQ3fV9PhwBSHO/ZbwJXM2Z0OPx+UJk= k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.16.4 h1:XMh7dF19MlyvMfQCHvH929YGg2WFrIuJ4N5sx3G7U+k= -sigs.k8s.io/controller-runtime v0.16.4/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/controller-runtime v0.16.5 h1:yr1cEJbX08xsTW6XEIzT13KHHmIyX8Umvme2cULvFZw= +sigs.k8s.io/controller-runtime v0.16.5/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/pkg/novaconductor/dbpurge.go b/pkg/novaconductor/dbpurge.go new file mode 100644 index 000000000..a06cd8821 --- /dev/null +++ b/pkg/novaconductor/dbpurge.go @@ -0,0 +1,93 @@ +package novaconductor + +import ( + "fmt" + + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + "github.com/openstack-k8s-operators/lib-common/modules/common/env" + novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + "github.com/openstack-k8s-operators/nova-operator/pkg/nova" +) + +func DBPurgeCronJob( + instance *novav1.NovaConductor, + labels map[string]string, + annotations map[string]string, +) *batchv1.CronJob { + args := []string{"-c", nova.KollaServiceCommand} + + envVars := map[string]env.Setter{} + envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") + envVars["KOLLA_BOOTSTRAP"] = env.SetValue("true") + + envVars["ARCHIVE_AGE"] = env.SetValue(fmt.Sprintf("%d", *instance.Spec.DBPurge.ArchiveAge)) + envVars["PURGE_AGE"] = env.SetValue(fmt.Sprintf("%d", *instance.Spec.DBPurge.PurgeAge)) + + env := env.MergeEnvs([]corev1.EnvVar{}, envVars) + + volumes := []corev1.Volume{ + nova.GetConfigVolume(nova.GetServiceConfigSecretName(instance.Name)), + nova.GetScriptVolume(nova.GetScriptSecretName(instance.Name)), + } + volumeMounts := []corev1.VolumeMount{ + nova.GetConfigVolumeMount(), + nova.GetScriptVolumeMount(), + nova.GetKollaConfigVolumeMount("nova-conductor-dbpurge"), + } + + // add CA cert if defined + if instance.Spec.TLS.CaBundleSecretName != "" { + volumes = append(volumes, instance.Spec.TLS.CreateVolume()) + volumeMounts = append(volumeMounts, instance.Spec.TLS.CreateVolumeMounts(nil)...) + } + + cron := &batchv1.CronJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: instance.Name + "-db-purge", + Namespace: instance.Namespace, + Labels: labels, + }, + Spec: batchv1.CronJobSpec{ + Schedule: *instance.Spec.DBPurge.Schedule, + ConcurrencyPolicy: batchv1.ForbidConcurrent, + JobTemplate: batchv1.JobTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: annotations, + }, + Spec: batchv1.JobSpec{ + Parallelism: ptr.To[int32](1), + Completions: ptr.To[int32](1), + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyOnFailure, + ServiceAccountName: instance.Spec.ServiceAccount, + Volumes: volumes, + Containers: []corev1.Container{ + { + Name: "nova-manage", + Command: []string{ + "/bin/bash", + }, + Args: args, + Image: instance.Spec.ContainerImage, + SecurityContext: &corev1.SecurityContext{ + RunAsUser: ptr.To(nova.NovaUserID), + }, + Env: env, + VolumeMounts: volumeMounts, + }, + }, + }, + }, + }, + }, + }, + } + + return cron +} diff --git a/templates/novaconductor/bin/dbpurge.sh b/templates/novaconductor/bin/dbpurge.sh new file mode 100755 index 000000000..2e02f8338 --- /dev/null +++ b/templates/novaconductor/bin/dbpurge.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# Copyright 2024. +# +# 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. + +set -ex +export ARCHIVE_AGE=${ARCHIVE_AGE:?"Please specify ARCHIVE_AGE variable."} +export PURGE_AGE=${PURGE_AGE:?"Please specify PURGE_AGE variable."} +archive_before=$(date --date="${ARCHIVE_AGE} day ago" +%Y-%m-%d) +purge_before=$(date --date="${PURGE_AGE} day ago" +%Y-%m-%d) + +nova-manage db archive_deleted_rows --verbose --until-complete --task-log --before "${archive_before}" +nova-manage db purge --verbose --before "${purge_before}" diff --git a/templates/novaconductor/config/nova-conductor-dbpurge-config.json b/templates/novaconductor/config/nova-conductor-dbpurge-config.json new file mode 100644 index 000000000..7adf60f94 --- /dev/null +++ b/templates/novaconductor/config/nova-conductor-dbpurge-config.json @@ -0,0 +1,37 @@ +{ + "command": "/bin/dbpurge.sh", + "config_files": [ + { + "source": "/var/lib/openstack/config/nova-blank.conf", + "dest": "/etc/nova/nova.conf", + "owner": "nova", + "perm": "0600" + }, + { + "source": "/var/lib/openstack/config/01-nova.conf", + "dest": "/etc/nova/nova.conf.d/01-nova.conf", + "owner": "nova", + "perm": "0600" + }, + { + "source": "/var/lib/openstack/config/02-nova-override.conf", + "dest": "/etc/nova/nova.conf.d/02-nova-override.conf", + "owner": "nova", + "perm": "0600", + "optional": true + }, + { + "source": "/var/lib/openstack/bin/dbpurge.sh", + "dest": "/bin/", + "owner": "nova", + "perm": "0700" + } + ], + "permissions": [ + { + "path": "/var/log/nova", + "owner": "nova:nova", + "recurse": true + } + ] +} diff --git a/test/functional/base_test.go b/test/functional/base_test.go index e7abc1bae..ddbe1efd7 100644 --- a/test/functional/base_test.go +++ b/test/functional/base_test.go @@ -21,8 +21,10 @@ import ( "time" "github.com/go-logr/logr" + "github.com/onsi/gomega" . "github.com/onsi/gomega" "golang.org/x/exp/maps" + batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" k8s_errors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" @@ -548,6 +550,7 @@ type CellNames struct { NovaComputeStatefulSetName types.NamespacedName NovaComputeConfigDataName types.NamespacedName HostDiscoveryJobName types.NamespacedName + DBPurgeCronJobName types.NamespacedName } func GetCellNames(novaName types.NamespacedName, cell string) CellNames { @@ -636,6 +639,10 @@ func GetCellNames(novaName types.NamespacedName, cell string) CellNames { Namespace: novaName.Namespace, Name: cellName.Name + "-compute-config", }, + DBPurgeCronJobName: types.NamespacedName{ + Namespace: novaName.Namespace, + Name: cellConductor.Name + "-db-purge", + }, } if cell == "cell0" { @@ -969,3 +976,12 @@ func AssertComputeDoesNotExist(name types.NamespacedName) { g.Expect(k8s_errors.IsNotFound(err)).To(BeTrue()) }, timeout, interval).Should(Succeed()) } + +func GetCronJob(name types.NamespacedName) *batchv1.CronJob { + cron := &batchv1.CronJob{} + gomega.Eventually(func(g gomega.Gomega) { + g.Expect(k8sClient.Get(ctx, name, cron)).Should(gomega.Succeed()) + }, timeout, interval).Should(gomega.Succeed()) + + return cron +} diff --git a/test/functional/nova_reconfiguration_test.go b/test/functional/nova_reconfiguration_test.go index b81d27f2e..d8a6c9ecc 100644 --- a/test/functional/nova_reconfiguration_test.go +++ b/test/functional/nova_reconfiguration_test.go @@ -644,4 +644,27 @@ var _ = Describe("Nova reconfiguration", func() { novaNames.MetadataName).Status.Hash[novaNames.MetadataNeutronConfigDataName.Name] Expect(originalComputeHash).NotTo(Equal(newComputeHash)) }) + + It("reconfigures DB Pruge job", func() { + Eventually(func(g Gomega) { + nova := GetNova(novaNames.NovaName) + cell0 := nova.Spec.CellTemplates["cell0"] + (&cell0).DBPurge.Schedule = ptr.To("3 0 * * *") + (&cell0).DBPurge.ArchiveAge = ptr.To(33) + (&cell0).DBPurge.PurgeAge = ptr.To(99) + + nova.Spec.CellTemplates["cell0"] = cell0 + + g.Expect(k8sClient.Update(ctx, nova)).To(Succeed()) + }, timeout, interval).Should(Succeed()) + + Eventually(func(g Gomega) { + cron := GetCronJob(cell0.DBPurgeCronJobName) + + g.Expect(cron.Spec.Schedule).To(Equal("3 0 * * *")) + jobEnv := cron.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Env + g.Expect(GetEnvVarValue(jobEnv, "ARCHIVE_AGE", "")).To(Equal("33")) + g.Expect(GetEnvVarValue(jobEnv, "PURGE_AGE", "")).To(Equal("99")) + }, timeout, interval).Should(Succeed()) + }) }) diff --git a/test/functional/novaconductor_controller_test.go b/test/functional/novaconductor_controller_test.go index 96a64b67a..5a5e75ca1 100644 --- a/test/functional/novaconductor_controller_test.go +++ b/test/functional/novaconductor_controller_test.go @@ -190,6 +190,10 @@ var _ = Describe("NovaConductor controller", func() { scriptData := string(scriptMap.Data["dbsync.sh"]) Expect(scriptData).Should(ContainSubstring("nova-manage db sync")) Expect(scriptData).Should(ContainSubstring("nova-manage api_db sync")) + Expect(scriptMap.Data).Should(HaveKey("dbpurge.sh")) + scriptData = string(scriptMap.Data["dbpurge.sh"]) + Expect(scriptData).Should(ContainSubstring("nova-manage db archive_deleted_rows")) + Expect(scriptData).Should(ContainSubstring("nova-manage db purge")) }) It("stored the input hash in the Status", func() { @@ -322,6 +326,27 @@ var _ = Describe("NovaConductor controller", func() { conductor := GetNovaConductor(cell0.ConductorName) Expect(conductor.Status.ReadyCount).To(BeNumerically(">", 0)) }) + + It("creates the DB purge CronJob", func() { + th.SimulateStatefulSetReplicaReady(cell0.ConductorStatefulSetName) + + conductor := GetNovaConductor(cell0.ConductorName) + cron := GetCronJob(cell0.DBPurgeCronJobName) + + Expect(cron.Spec.Schedule).To(Equal(*conductor.Spec.DBPurge.Schedule)) + jobEnv := cron.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Env + Expect(GetEnvVarValue(jobEnv, "ARCHIVE_AGE", "")).To( + Equal(fmt.Sprintf("%d", *conductor.Spec.DBPurge.ArchiveAge))) + Expect(GetEnvVarValue(jobEnv, "PURGE_AGE", "")).To( + Equal(fmt.Sprintf("%d", *conductor.Spec.DBPurge.PurgeAge))) + + th.ExpectCondition( + cell0.ConductorName, + ConditionGetterFunc(NovaConductorConditionGetter), + condition.CronJobReadyCondition, + corev1.ConditionTrue, + ) + }) }) })