Skip to content

Commit

Permalink
Add initial watcher api conf generation
Browse files Browse the repository at this point in the history
Implement an initial version of watcher config generation. This change
adds a watcher config template and generates a secret with the templated
config. Some fields that require changes in the watcher controller like
the transporturl and memcached servers.

This change also modifies the WatcherAPI functional tests so the
WatcherAPI instances use a different name that the Watcher one, so it's
easier to debug.
  • Loading branch information
cescgina committed Dec 18, 2024
1 parent eceb232 commit 40953e8
Show file tree
Hide file tree
Showing 26 changed files with 681 additions and 41 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 @@ -49,6 +49,11 @@ spec:
MariaDB instance name
Required to use the mariadb-operator instance to create the DB and user
type: string
memcachedInstance:
default: memcached
description: MemcachedInstance is the name of the Memcached CR that
all watcher service will use.
type: string
passwordSelectors:
default:
service: WatcherPassword
Expand Down
5 changes: 5 additions & 0 deletions api/bases/watcher.openstack.org_watchers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ spec:
MariaDB instance name
Required to use the mariadb-operator instance to create the DB and user
type: string
memcachedInstance:
default: memcached
description: MemcachedInstance is the name of the Memcached CR that
all watcher service will use.
type: string
passwordSelectors:
default:
service: WatcherPassword
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 @@ -38,6 +38,11 @@ type WatcherCommon struct {
// +kubebuilder:default=watcher
// DatabaseAccount - MariaDBAccount CR name used for watcher DB, defaults to watcher
DatabaseAccount string `json:"databaseAccount"`

// +kubebuilder:validation:Optional
// +kubebuilder:default=memcached
// MemcachedInstance is the name of the Memcached CR that all watcher service will use.
MemcachedInstance string `json:"memcachedInstance"`
}

// WatcherTemplate defines the fields used in the top level CR
Expand Down
1 change: 1 addition & 0 deletions api/v1beta1/watcherapi_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type WatcherAPISpec struct {
// Important: Run "make" to regenerate code after modifying this file

WatcherCommon `json:",inline"`

// +kubebuilder:validation:Required
// Secret containing all passwords / keys needed
Secret string `json:"secret"`
Expand Down
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 @@ -49,6 +49,11 @@ spec:
MariaDB instance name
Required to use the mariadb-operator instance to create the DB and user
type: string
memcachedInstance:
default: memcached
description: MemcachedInstance is the name of the Memcached CR that
all watcher service will use.
type: string
passwordSelectors:
default:
service: WatcherPassword
Expand Down
5 changes: 5 additions & 0 deletions config/crd/bases/watcher.openstack.org_watchers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ spec:
MariaDB instance name
Required to use the mariadb-operator instance to create the DB and user
type: string
memcachedInstance:
default: memcached
description: MemcachedInstance is the name of the Memcached CR that
all watcher service will use.
type: string
passwordSelectors:
default:
service: WatcherPassword
Expand Down
25 changes: 25 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ rules:
- patch
- update
- watch
- apiGroups:
- keystone.openstack.org
resources:
- keystoneapis
verbs:
- get
- list
- watch
- apiGroups:
- keystone.openstack.org
resources:
Expand Down Expand Up @@ -82,6 +90,23 @@ rules:
- patch
- update
- watch
- apiGroups:
- memcached.openstack.org
resources:
- memcacheds
verbs:
- get
- list
- patch
- update
- watch
- apiGroups:
- memcached.openstack.org
resources:
- memcacheds/finalizers
verbs:
- patch
- update
- apiGroups:
- rabbitmq.openstack.org
resources:
Expand Down
79 changes: 79 additions & 0 deletions controllers/watcher_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"

memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1"
"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/secret"
"github.com/openstack-k8s-operators/lib-common/modules/common/util"
)

Expand Down Expand Up @@ -188,3 +192,78 @@ func ensureSecret(

return hash, ctrl.Result{}, *secret, nil
}

func GenerateConfigsGeneric(
ctx context.Context, helper *helper.Helper,
instance client.Object,
envVars *map[string]env.Setter,
templateParameters map[string]interface{},
customData map[string]string,
cmLabels map[string]string,
scripts bool,
) error {

cms := []util.Template{
// Templates where the watcher config is stored
{
Name: fmt.Sprintf("%s-config-data", instance.GetName()),
Namespace: instance.GetNamespace(),
Type: util.TemplateTypeConfig,
InstanceType: instance.GetObjectKind().GroupVersionKind().Kind,
ConfigOptions: templateParameters,
CustomData: customData,
Labels: cmLabels,
},
}
if scripts {
cms = append(cms, util.Template{
Name: fmt.Sprintf("%s-scripts", instance.GetName()),
Namespace: instance.GetNamespace(),
Type: util.TemplateTypeScripts,
InstanceType: instance.GetObjectKind().GroupVersionKind().Kind,
ConfigOptions: templateParameters,
Labels: cmLabels,
})
}
return secret.EnsureSecrets(ctx, helper, instance, cms, envVars)
}

// ensureMemcached - gets the Memcached instance used for watcher services cache backend
func ensureMemcached(
ctx context.Context,
helper *helper.Helper,
namespaceName string,
memcachedName string,
conditionUpdater conditionUpdater,
) (*memcachedv1.Memcached, error) {
memcached, err := memcachedv1.GetMemcachedByName(ctx, helper, memcachedName, namespaceName)
if err != nil {
if k8s_errors.IsNotFound(err) {
conditionUpdater.Set(condition.FalseCondition(
condition.MemcachedReadyCondition,
condition.RequestedReason,
condition.SeverityInfo,
condition.MemcachedReadyWaitingMessage))
return nil, fmt.Errorf("memcached %s not found", memcachedName)
}
conditionUpdater.Set(condition.FalseCondition(
condition.MemcachedReadyCondition,
condition.ErrorReason,
condition.SeverityWarning,
condition.MemcachedReadyErrorMessage,
err.Error()))
return nil, err
}

if !memcached.IsReady() {
conditionUpdater.Set(condition.FalseCondition(
condition.MemcachedReadyCondition,
condition.RequestedReason,
condition.SeverityInfo,
condition.MemcachedReadyWaitingMessage))
return nil, fmt.Errorf("memcached %s is not ready", memcachedName)
}
conditionUpdater.MarkTrue(condition.MemcachedReadyCondition, condition.MemcachedReadyMessage)

return memcached, err
}
93 changes: 82 additions & 11 deletions controllers/watcherapi_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,14 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"

"github.com/go-logr/logr"
memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1"
keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
"github.com/openstack-k8s-operators/lib-common/modules/common/condition"
"github.com/openstack-k8s-operators/lib-common/modules/common/endpoint"
"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/labels"
"github.com/openstack-k8s-operators/lib-common/modules/common/service"
mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1"

watcherv1beta1 "github.com/openstack-k8s-operators/watcher-operator/api/v1beta1"
Expand All @@ -59,6 +64,11 @@ func (r *WatcherAPIReconciler) GetLogger(ctx context.Context) logr.Logger {
//+kubebuilder:rbac:groups=watcher.openstack.org,resources=watcherapis/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=watcher.openstack.org,resources=watcherapis/finalizers,verbs=update
//+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete;
//+kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneapis,verbs=get;list;watch;
//+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=memcached.openstack.org,resources=memcacheds,verbs=get;list;watch;update;patch
//+kubebuilder:rbac:groups=memcached.openstack.org,resources=memcacheds/finalizers,verbs=update;patch

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
Expand Down Expand Up @@ -138,6 +148,7 @@ func (r *WatcherAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request)
types.NamespacedName{Namespace: instance.Namespace, Name: instance.Spec.Secret},
[]string{
instance.Spec.PasswordSelectors.Service,
TransportURLSelector,
},
helper.GetClient(),
&instance.Status.Conditions,
Expand All @@ -163,7 +174,26 @@ func (r *WatcherAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request)
// all our input checks out so report InputReady
instance.Status.Conditions.MarkTrue(condition.InputReadyCondition, condition.InputReadyMessage)

err = r.generateServiceConfigs(ctx, instance, secret, db, helper, &configVars)
memcached, err := ensureMemcached(ctx, helper, instance.Namespace, instance.Spec.MemcachedInstance, &instance.Status.Conditions)

if err != nil {
return ctrl.Result{}, err
}
// Add finalizer to Memcached to prevent it from being deleted now that we're using it
if controllerutil.AddFinalizer(memcached, helper.GetFinalizer()) {
err := helper.GetClient().Update(ctx, memcached)
if err != nil {
instance.Status.Conditions.Set(condition.FalseCondition(
condition.MemcachedReadyCondition,
condition.ErrorReason,
condition.SeverityWarning,
condition.MemcachedReadyErrorMessage,
err.Error()))
return ctrl.Result{}, err
}
}

err = r.generateServiceConfigs(ctx, instance, secret, db, memcached, helper, &configVars)
if err != nil {
return ctrl.Result{}, err
}
Expand All @@ -181,25 +211,65 @@ func (r *WatcherAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request)
}

// generateServiceConfigs - create Secret which holds the service configuration
// NOTE - jgilaber this function is WIP, currently implements a fraction of its
// functionality and will be expanded of further iteration to actually generate
// the service configs
func (r *WatcherAPIReconciler) generateServiceConfigs(
ctx context.Context, instance *watcherv1beta1.WatcherAPI,
secret corev1.Secret, db *mariadbv1.Database,
memcachedInstance *memcachedv1.Memcached,
helper *helper.Helper, envVars *map[string]env.Setter,
) error {
Log := r.GetLogger(ctx)
Log.Info("generateServiceConfigs - reconciling")

// replace by actual usage in future iterations
_ = db
_ = helper
_ = instance
_ = secret
_ = envVars
labels := labels.GetLabels(instance, labels.GetGroupLabel(watcher.ServiceName), map[string]string{})
// jgilaber this might be wrong? we should probably get keystonapi in the
// watcher controller and set the url in the spec eventually?
keystoneAPI, err := keystonev1.GetKeystoneAPI(ctx, helper, instance.Namespace, map[string]string{})
// KeystoneAPI not available we should not aggregate the error and continue
if err != nil {
instance.Status.Conditions.Set(condition.FalseCondition(
condition.ServiceConfigReadyCondition,
condition.ErrorReason,
condition.SeverityWarning,
condition.ServiceConfigReadyErrorMessage,
"keystoneAPI not found"))
return err
}
keystoneInternalURL, err := keystoneAPI.GetEndpoint(endpoint.EndpointInternal)
if err != nil {
return err
}
// customData hold any customization for the service.
// NOTE jgilaber making an empty map for now, we'll probably want to
// implement CustomServiceConfig later
customData := 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,
),
"KeystoneAuthURL": keystoneInternalURL,
"ServicePassword": string(secret.Data[instance.Spec.PasswordSelectors.Service]),
"ServiceUser": instance.Spec.ServiceUser,
"TransportURL": string(secret.Data[TransportURLSelector]),
"MemcachedServers": memcachedInstance.GetMemcachedServerListString(),
}

return nil
// create httpd vhost template parameters
httpdVhostConfig := map[string]interface{}{}
for _, endpt := range []service.Endpoint{service.EndpointInternal, service.EndpointPublic} {
endptConfig := map[string]interface{}{}
endptConfig["ServerName"] = fmt.Sprintf("%s-%s.%s.svc", watcher.ServiceName, endpt.String(), instance.Namespace)
endptConfig["TLS"] = false // default TLS to false, and set it below when implemented
httpdVhostConfig[endpt.String()] = endptConfig
}
templateParameters["VHosts"] = httpdVhostConfig

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

func (r *WatcherAPIReconciler) reconcileDelete(ctx context.Context, instance *watcherv1beta1.WatcherAPI, helper *helper.Helper) (ctrl.Result, error) {
Expand All @@ -221,6 +291,7 @@ func (r *WatcherAPIReconciler) initStatus(instance *watcherv1beta1.WatcherAPI) e
condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage),
condition.UnknownCondition(condition.InputReadyCondition, condition.InitReason, condition.InputReadyInitMessage),
condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyMessage),
condition.UnknownCondition(condition.MemcachedReadyCondition, condition.InitReason, condition.MemcachedReadyInitMessage),
)

instance.Status.Conditions.Init(&cl)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
k8s.io/api v0.29.10
k8s.io/apimachinery v0.29.10
k8s.io/client-go v0.29.10
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
sigs.k8s.io/controller-runtime v0.17.6
)

Expand Down Expand Up @@ -75,7 +76,6 @@ require (
k8s.io/component-base v0.29.10 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"

memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1"
rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1"
keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1"
Expand All @@ -58,6 +59,7 @@ func init() {
utilruntime.Must(mariadbv1.AddToScheme(scheme))
utilruntime.Must(rabbitmqv1.AddToScheme(scheme))
utilruntime.Must(keystonev1.AddToScheme(scheme))
utilruntime.Must(memcachedv1.AddToScheme(scheme))
//+kubebuilder:scaffold:scheme
}

Expand Down
Loading

0 comments on commit 40953e8

Please sign in to comment.