Skip to content

Commit

Permalink
Create database schema for Watcher database
Browse files Browse the repository at this point in the history
This commit is implementing db schema creation (dbsync) via Job
following the same pattern as other operators.

It introduces some spec and status changes required and initial
configuration with only the required configuration required to run the
dbsync (just the database connection and transport which will be
required later to pass it to the subCRs).

This patch is also adding a new functional scenario test to test the
Watcher controller when using non-default values.

Finally, this patch is also skipping two new error types in the
crd-schema-checker as it complains about perserveJobs for being a
boolean type and Hash to be a map. However we want to keep them with
those types for consistency with the rest of operators.
  • Loading branch information
amoralej authored and openshift-merge-bot[bot] committed Dec 20, 2024
1 parent 13244ed commit 74a3456
Show file tree
Hide file tree
Showing 18 changed files with 601 additions and 2 deletions.
5 changes: 5 additions & 0 deletions api/bases/watcher.openstack.org_watcherapis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ spec:
password from the Secret
type: string
type: object
preserveJobs:
default: false
description: PreserveJobs - do not delete jobs after they finished
e.g. to check logs
type: boolean
secret:
description: Secret containing all passwords / keys needed
type: string
Expand Down
10 changes: 10 additions & 0 deletions api/bases/watcher.openstack.org_watchers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ spec:
password from the Secret
type: string
type: object
preserveJobs:
default: false
description: PreserveJobs - do not delete jobs after they finished
e.g. to check logs
type: boolean
rabbitMqClusterName:
default: rabbitmq
description: |-
Expand Down Expand Up @@ -142,6 +147,11 @@ spec:
- type
type: object
type: array
hash:
additionalProperties:
type: string
description: Map of hashes to track e.g. job status
type: object
observedGeneration:
description: |-
ObservedGeneration - the most recent generation observed for this
Expand Down
5 changes: 5 additions & 0 deletions api/v1beta1/common_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ type WatcherCommon struct {
// +kubebuilder:default=memcached
// MemcachedInstance is the name of the Memcached CR that all watcher service will use.
MemcachedInstance string `json:"memcachedInstance"`

// +kubebuilder:validation:Optional
// +kubebuilder:default=false
// PreserveJobs - do not delete jobs after they finished e.g. to check logs
PreserveJobs bool `json:"preserveJobs"`
}

// WatcherTemplate defines the fields used in the top level CR
Expand Down
9 changes: 8 additions & 1 deletion api/v1beta1/watcher_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,18 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
// DbSyncHash hash
DbSyncHash = "dbsync"
)

// WatcherSpec defines the desired state of Watcher
type WatcherSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file

WatcherTemplate `json:",inline"`

// +kubebuilder:validation:Required
WatcherImages `json:",inline"`
}

Expand All @@ -40,6 +44,9 @@ type WatcherStatus struct {
// ServiceID - The ID of the watcher service registered in keystone
ServiceID string `json:"serviceID,omitempty"`

// Map of hashes to track e.g. job status
Hash map[string]string `json:"hash,omitempty"`

// ObservedGeneration - the most recent generation observed for this
// service. If the observed generation is less than the spec generation,
// then the controller has not processed the latest changes injected by
Expand Down
7 changes: 7 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions config/crd/bases/watcher.openstack.org_watcherapis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ spec:
password from the Secret
type: string
type: object
preserveJobs:
default: false
description: PreserveJobs - do not delete jobs after they finished
e.g. to check logs
type: boolean
secret:
description: Secret containing all passwords / keys needed
type: string
Expand Down
10 changes: 10 additions & 0 deletions config/crd/bases/watcher.openstack.org_watchers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ spec:
password from the Secret
type: string
type: object
preserveJobs:
default: false
description: PreserveJobs - do not delete jobs after they finished
e.g. to check logs
type: boolean
rabbitMqClusterName:
default: rabbitmq
description: |-
Expand Down Expand Up @@ -142,6 +147,11 @@ spec:
- type
type: object
type: array
hash:
additionalProperties:
type: string
description: Map of hashes to track e.g. job status
type: object
observedGeneration:
description: |-
ObservedGeneration - the most recent generation observed for this
Expand Down
12 changes: 12 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ rules:
- patch
- update
- watch
- apiGroups:
- batch
resources:
- jobs
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- ""
resources:
Expand Down
140 changes: 140 additions & 0 deletions controllers/watcher_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"time"

"github.com/go-logr/logr"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/types"
Expand All @@ -34,7 +35,10 @@ import (
keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
"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/env"
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
"github.com/openstack-k8s-operators/lib-common/modules/common/job"
"github.com/openstack-k8s-operators/lib-common/modules/common/labels"
common_rbac "github.com/openstack-k8s-operators/lib-common/modules/common/rbac"
"github.com/openstack-k8s-operators/lib-common/modules/common/util"
mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1"
Expand Down Expand Up @@ -77,6 +81,7 @@ func (r *WatcherReconciler) GetLogger(ctx context.Context) logr.Logger {
//+kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneservices,verbs=get;list;watch;create;update;patch;delete;
//+kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneendpoints,verbs=get;list;watch;create;update;patch;delete;
//+kubebuilder:rbac:groups="",resources=pods,verbs=create;delete;get;list;patch;update;watch
//+kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;update;patch;delete;

// service account, role, rolebinding
//+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update;patch
Expand Down Expand Up @@ -224,6 +229,21 @@ func (r *WatcherReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re
return ctrl.Result{}, errors.New("error retrieving required data from secret")
}

hashTransporturl, _, transporturlSecret, err := ensureSecret(
ctx,
types.NamespacedName{Namespace: instance.Namespace, Name: transportURL.Status.SecretName},
[]string{
TransportURLSelector,
},
helper.GetClient(),
&instance.Status.Conditions,
r.RequeueTimeout,
)
if err != nil || hashTransporturl == "" {
// Empty hash means that there is some problem retrieving the key from the secret
return ctrl.Result{}, errors.New("error retrieving required data from transporturl secret")
}

instance.Status.Conditions.MarkTrue(condition.InputReadyCondition, condition.InputReadyMessage)
// End of Input Ready check

Expand All @@ -237,6 +257,31 @@ func (r *WatcherReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re

// End of Keystone service creation

// Generate config for dbsync
configVars := make(map[string]env.Setter)

err = r.generateServiceConfigDBSync(ctx, instance, db, &transporturlSecret, helper, &configVars)
if err != nil {
instance.Status.Conditions.Set(condition.FalseCondition(
condition.ServiceConfigReadyCondition,
condition.ErrorReason,
condition.SeverityWarning,
condition.ServiceConfigReadyErrorMessage,
err.Error()))
return ctrl.Result{}, err
}

instance.Status.Conditions.MarkTrue(condition.ServiceConfigReadyCondition, condition.ServiceConfigReadyMessage)
// End of config generation for dbsync

ctrlResult, err := r.ensureDBSync(ctx, helper, instance, serviceLabels)
if err != nil {
return ctrlResult, err
} else if (ctrlResult != ctrl.Result{}) {
return ctrlResult, nil
}

//
// remove finalizers from unused MariaDBAccount records
// this assumes all database-depedendent deployments are up and
// running with current database account info
Expand Down Expand Up @@ -268,6 +313,11 @@ func (r *WatcherReconciler) initStatus(instance *watcherv1beta1.Watcher) error {
// Update the lastObserved generation before evaluating conditions
instance.Status.ObservedGeneration = instance.Generation

// initialize .Status.Hash
if instance.Status.Hash == nil {
instance.Status.Hash = make(map[string]string)
}

return nil
}

Expand Down Expand Up @@ -308,6 +358,14 @@ func (r *WatcherReconciler) initConditions(instance *watcherv1beta1.Watcher) err
condition.RoleBindingReadyCondition,
condition.InitReason,
condition.RoleBindingReadyInitMessage),
condition.UnknownCondition(
condition.ServiceConfigReadyCondition,
condition.InitReason,
condition.ServiceConfigReadyInitMessage),
condition.UnknownCondition(
condition.DBSyncReadyCondition,
condition.InitReason,
condition.DBSyncReadyInitMessage),
)

instance.Status.Conditions.Init(&cl)
Expand Down Expand Up @@ -544,6 +602,87 @@ func (r *WatcherReconciler) ensureKeystoneSvc(
return ctrlResult, nil
}

func (r *WatcherReconciler) generateServiceConfigDBSync(
ctx context.Context,
instance *watcherv1beta1.Watcher,
db *mariadbv1.Database,
transporturlSecret *corev1.Secret,
helper *helper.Helper,
envVars *map[string]env.Setter,
) error {
Log := r.GetLogger(ctx)
Log.Info("generateServiceConfigs - reconciling config for Watcher CR")

customData := map[string]string{}

labels := labels.GetLabels(instance, labels.GetGroupLabel(watcher.ServiceName), map[string]string{})
databaseAccount := db.GetAccount()
databaseSecret := db.GetSecret()
templateParameters := map[string]interface{}{
"DatabaseConnection": fmt.Sprintf("mysql+pymysql://%s:%s@%s/%s?charset=utf8",
databaseAccount.Spec.UserName,
string(databaseSecret.Data[mariadbv1.DatabasePasswordSelector]),
db.GetDatabaseHostname(),
watcher.DatabaseName,
),
"TransportURL": string(transporturlSecret.Data[TransportURLSelector]),
}

return GenerateConfigsGeneric(ctx, helper, instance, envVars, templateParameters, customData, labels, false)
}

func (r *WatcherReconciler) ensureDBSync(
ctx context.Context,
h *helper.Helper,
instance *watcherv1beta1.Watcher,
serviceLabels map[string]string,
) (ctrl.Result, error) {
Log := r.GetLogger(ctx)
Log.Info(fmt.Sprintf("Reconciling the Keystone Service for '%s'", instance.Name))

// So far we are not using Service Annotations
dbSyncHash := instance.Status.Hash[watcherv1beta1.DbSyncHash]
jobDef := watcher.DbSyncJob(instance, serviceLabels, nil)

dbSyncjob := job.NewJob(
jobDef,
watcherv1beta1.DbSyncHash,
instance.Spec.PreserveJobs,
time.Duration(5)*time.Second,
dbSyncHash,
)

ctrlResult, err := dbSyncjob.DoJob(
ctx,
h,
)

if (ctrlResult != ctrl.Result{}) {
instance.Status.Conditions.Set(condition.FalseCondition(
condition.DBSyncReadyCondition,
condition.RequestedReason,
condition.SeverityInfo,
condition.DBSyncReadyRunningMessage))
return ctrlResult, nil
}
if err != nil {
instance.Status.Conditions.Set(condition.FalseCondition(
condition.DBSyncReadyCondition,
condition.ErrorReason,
condition.SeverityWarning,
condition.DBSyncReadyErrorMessage,
err.Error()))
return ctrl.Result{}, err
}
if dbSyncjob.HasChanged() {
instance.Status.Hash[watcherv1beta1.DbSyncHash] = dbSyncjob.GetHash()
Log.Info(fmt.Sprintf("Service '%s' - Job %s hash added - %s", instance.Name, jobDef.Name, instance.Status.Hash[watcherv1beta1.DbSyncHash]))
}
instance.Status.Conditions.MarkTrue(condition.DBSyncReadyCondition, condition.DBSyncReadyMessage)

return ctrlResult, nil
}

func (r *WatcherReconciler) reconcileDelete(ctx context.Context, instance *watcherv1beta1.Watcher, helper *helper.Helper) (ctrl.Result, error) {
Log := r.GetLogger(ctx)
Log.Info(fmt.Sprintf("Reconcile Service '%s' delete started", instance.Name))
Expand Down Expand Up @@ -595,5 +734,6 @@ func (r *WatcherReconciler) SetupWithManager(mgr ctrl.Manager) error {
Owns(&corev1.ServiceAccount{}).
Owns(&rbacv1.Role{}).
Owns(&rbacv1.RoleBinding{}).
Owns(&batchv1.Job{}).
Complete(r)
}
2 changes: 1 addition & 1 deletion hack/crd-schema-checker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ for crd in config/crd/bases/*.yaml; do
git show "$BASE_REF:$crd" > "$TMP_DIR/$crd"
$CHECKER check-manifests \
--existing-crd-filename="$TMP_DIR/$crd" \
--disabled-validators="NoFieldRemoval,NoNewRequiredFields,ConditionsMustHaveProperSSATags,ListsMustHaveSSATags" \
--disabled-validators="NoFieldRemoval,NoNewRequiredFields,ConditionsMustHaveProperSSATags,ListsMustHaveSSATags,NoBools,NoMaps" \
--new-crd-filename="$crd"
done
9 changes: 9 additions & 0 deletions pkg/watcher/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,13 @@ const (

// DatabaseCRName - name of the CR used to create the Watcher database
DatabaseCRName = "watcher"

// DefaultsConfigFileName - File name with default configuration
DefaultsConfigFileName = "00-default.conf"

// CustomConfigFileName - File name with custom configuration
CustomConfigFileName = "01-custom.conf"

// LogVolume is the default logVolume name used to mount logs
LogVolume = "logs"
)
Loading

0 comments on commit 74a3456

Please sign in to comment.