drupalsite_resources.go 69.9 KB
Newer Older
1
/*
2
Copyright 2021 CERN.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

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.
*/

package controllers

import (
	"context"
21
22
	"crypto/md5"
	"encoding/hex"
23
	"fmt"
24
	"io/ioutil"
25
	"math/rand"
26
	"net/url"
Dimitra Chatzichrysou's avatar
Dimitra Chatzichrysou committed
27
	"path"
28
	"strconv"
29
	"time"
30
31

	"github.com/go-logr/logr"
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
32
33
	buildv1 "github.com/openshift/api/build/v1"
	imagev1 "github.com/openshift/api/image/v1"
34
	routev1 "github.com/openshift/api/route/v1"
35

36
	dbodv1a1 "gitlab.cern.ch/drupal/paas/dbod-operator/api/v1alpha1"
37
	webservicesv1a1 "gitlab.cern.ch/drupal/paas/drupalsite-operator/api/v1alpha1"
38
	authz "gitlab.cern.ch/paas-tools/operators/authz-operator/api/v1alpha1"
39
	appsv1 "k8s.io/api/apps/v1"
40
	batchv1 "k8s.io/api/batch/v1"
41
	corev1 "k8s.io/api/core/v1"
Dimitra Chatzichrysou's avatar
Dimitra Chatzichrysou committed
42
	v1 "k8s.io/api/core/v1"
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
43
	rbacv1 "k8s.io/api/rbac/v1"
44
	k8sapierrors "k8s.io/apimachinery/pkg/api/errors"
45
46
47
48
49
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/utils/pointer"
50
	controllerruntime "sigs.k8s.io/controller-runtime"
51
	"sigs.k8s.io/controller-runtime/pkg/client"
52

53
	pipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
54
	velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
55
56
)

57
58
59
60
// Const vars
const (
	// Variable used to define Default WebDAV login Username
	webDAVDefaultLogin string = "admin"
61
	// Variable to set the used Memory for all Jobs generated by the Operator
62
	jobMemoryRequest string = "512Mi"
63
64
)

65
66
67
68
69
var (
	// BuildResources are the resource requests/limits for the image builds. Set during initEnv()
	BuildResources corev1.ResourceRequirements
)

70
/*
71
72
ensureResources ensures the presence of all the resources that the DrupalSite needs to serve content.
This includes BuildConfigs/ImageStreams, DB, PVC, PHP/Nginx deployment + service, site install job, Routes.
73
*/
74
func (r *DrupalSiteReconciler) ensureResources(drp *webservicesv1a1.DrupalSite, deploymentConfig DeploymentConfig, log logr.Logger) (transientErrs []reconcileError) {
75
	ctx := context.TODO()
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
76
77

	// 1. BuildConfigs and ImageStreams
78
79
	// TODO: Remove logic for ExtraConfigurationRepo once we deprecate the field
	if len(drp.Spec.Configuration.ExtraConfigurationRepo) > 0 || len(drp.Spec.Configuration.ExtraConfigurationRepository.RepositoryUrl) > 0 {
80
		if transientErr := r.ensureResourceX(ctx, drp, "is_s2i", log); transientErr != nil {
81
			transientErrs = append(transientErrs, transientErr.Wrap("%v: for S2I SiteBuilder ImageStream"))
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
82
		}
83
		if transientErr := r.ensureResourceX(ctx, drp, "bc_s2i", log); transientErr != nil {
84
			transientErrs = append(transientErrs, transientErr.Wrap("%v: for S2I SiteBuilder BuildConfig"))
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
85
		}
86
87
88
		if transientErr := r.ensureResourceX(ctx, drp, "gitlab_trigger_secret", log); transientErr != nil {
			transientErrs = append(transientErrs, transientErr.Wrap("%v: for S2I SiteBuilder Secret"))
		}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
89
90
91
	}
	// 2. Data layer

92
	if transientErr := r.ensureResourceX(ctx, drp, "pvc_drupal", log); transientErr != nil {
93
		transientErrs = append(transientErrs, transientErr.Wrap("%v: for Drupal PVC"))
94
	}
95
	if transientErr := r.ensureResourceX(ctx, drp, "dbod_cr", log); transientErr != nil {
96
		transientErrs = append(transientErrs, transientErr.Wrap("%v: for DBOD resource"))
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
97
	}
98
	if transientErr := r.ensureResourceX(ctx, drp, "webdav_secret", log); transientErr != nil {
99
100
		transientErrs = append(transientErrs, transientErr.Wrap("%v: for WebDAV Secret"))
	}
101

Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
102
103
	// 3. Serving layer

104
	if transientErr := r.ensureResourceX(ctx, drp, "cm_php", log); transientErr != nil {
105
		transientErrs = append(transientErrs, transientErr.Wrap("%v: for PHP-FPM CM"))
106
	}
107
	if transientErr := r.ensureResourceX(ctx, drp, "cm_nginx_global", log); transientErr != nil {
108
		transientErrs = append(transientErrs, transientErr.Wrap("%v: for Nginx CM"))
109
	}
110
	if transientErr := r.ensureResourceX(ctx, drp, "cm_settings", log); transientErr != nil {
111
112
		transientErrs = append(transientErrs, transientErr.Wrap("%v: for settings.php CM"))
	}
113
	if transientErr := r.ensureResourceX(ctx, drp, "cm_php_cli", log); transientErr != nil {
114
115
		transientErrs = append(transientErrs, transientErr.Wrap("%v: for PHP Job CM"))
	}
116
	if r.isDBODProvisioned(ctx, drp) {
117
		if transientErr := r.ensureDrupalDeployment(ctx, drp, deploymentConfig, log); transientErr != nil {
118
			transientErrs = append(transientErrs, transientErr.Wrap("%v: for Drupal deployment"))
119
		}
120
	}
121
	if transientErr := r.ensureResourceX(ctx, drp, "svc_nginx", log); transientErr != nil {
122
		transientErrs = append(transientErrs, transientErr.Wrap("%v: for Nginx SVC"))
123
	}
124
	/* A new drupalsite can be initialized with 3 different ways depending its Spec:
Konstantinos Samaras-Tsakiris's avatar
format    
Konstantinos Samaras-Tsakiris committed
125
126
127
128
129
		- clone_job if Spec.Configuration.CloneFrom is given
		- easystart_taskrun if Spec.Configuration.Easystart equals to enable
		- site_install_job if it is a fresh site
	        Between CloneFrom and Easystart we don't care which case is checked first (undefined).
	        We use an OPA rule that prohibits both fields from being set at the same time.
130
	*/
131
	if r.isDBODProvisioned(ctx, drp) && !(drp.ConditionTrue("Initialized")) {
132
133
		switch {
		case drp.Spec.Configuration.CloneFrom != "":
134
			if transientErr := r.ensureResourceX(ctx, drp, "clone_job", log); transientErr != nil {
Dimitra Chatzichrysou's avatar
Dimitra Chatzichrysou committed
135
				transientErrs = append(transientErrs, transientErr.Wrap("%v: for clone Job"))
Dimitra Chatzichrysou's avatar
Dimitra Chatzichrysou committed
136
			}
137
		case drp.Spec.Configuration.Easystart == "enable":
138
139
140
			if transientErr := r.ensureResourceX(ctx, drp, "easystart_taskrun", log); transientErr != nil {
				transientErrs = append(transientErrs, transientErr.Wrap("%v: for easystart TaskRun"))
			}
141
		default:
142
143
144
			if transientErr := r.ensureResourceX(ctx, drp, "site_install_job", log); transientErr != nil {
				transientErrs = append(transientErrs, transientErr.Wrap("%v: for site install Job"))
			}
145
146
		}
	}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
147
148
149

	// 4. Ingress

150
	if drp.ConditionTrue("Initialized") {
151
		// each function below ensures 1 route per entry in `spec.siteUrl[]`. This is understandably part of the job of "ensuring resource X".
152
		if transientErr := r.ensureResourceX(ctx, drp, "route", log); transientErr != nil {
153
154
			transientErrs = append(transientErrs, transientErr.Wrap("%v: for Route"))
		}
155
		if transientErr := r.ensureResourceX(ctx, drp, "oidc_return_uri", log); transientErr != nil {
156
157
			transientErrs = append(transientErrs, transientErr.Wrap("%v: for OidcReturnURI"))
		}
158
159
160

		// each function below removes any unwanted routes
		if transientErr := r.ensureNoExtraRouteResource(ctx, drp, "drupal", log); transientErr != nil {
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
161
			transientErrs = append(transientErrs, transientErr.Wrap("%v: while ensuring no extra routes"))
162
163
		}
		if transientErr := r.ensureNoExtraOidcReturnUriResource(ctx, drp, "drupal", log); transientErr != nil {
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
164
			transientErrs = append(transientErrs, transientErr.Wrap("%v: while ensuring no extra OidcReturnURIs"))
165
		}
166
	} else {
167
168
169
170
171
172
173
		for _, url := range drp.Spec.SiteURL {
			if transientErr := r.ensureNoRoute(ctx, drp, string(url), log); transientErr != nil {
				transientErrs = append(transientErrs, transientErr.Wrap("%v: while deleting the Route"))
			}
			if transientErr := r.ensureNoReturnURI(ctx, drp, string(url), log); transientErr != nil {
				transientErrs = append(transientErrs, transientErr.Wrap("%v: while deleting the OidcReturnURI"))
			}
174
		}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
175
	}
176

177
	// 5. Cluster-scoped: Backup schedule, Tekton RBAC
178
179
	// Create Velero schedule only after site is initialized in order for the first backup to not report 'Failed' or 'PartiallyFailed' status
	if drp.ConditionTrue("Initialized") && (drp.Status.IsPrimary || drp.Spec.Configuration.ScheduledBackups == "enabled") {
180
181
182
183
184
185
186
		if transientErr := r.ensureResourceX(ctx, drp, "backup_schedule", log); transientErr != nil {
			transientErrs = append(transientErrs, transientErr.Wrap("%v: for Velero Schedule"))
		}
	} else {
		if transientErr := r.ensureNoBackupSchedule(ctx, drp, log); transientErr != nil {
			transientErrs = append(transientErrs, transientErr.Wrap("%v: while deleting the Velero schedule"))
		}
187
	}
188
	if transientErr := r.ensureResourceX(ctx, drp, "tekton_extra_perm_rbac", log); transientErr != nil {
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
189
190
		transientErrs = append(transientErrs, transientErr.Wrap("%v: for Tekton Extra Permissions ClusterRoleBinding"))
	}
191
	return transientErrs
192
193
}

194
195
196
/*
ensureResourceX ensure the requested resource is created, with the following valid values
	- pvc_drupal: PersistentVolume for the drupalsite
197
	- site_install_job: Kubernetes Job for the drush ensure-site-install
Dimitra Chatzichrysou's avatar
Dimitra Chatzichrysou committed
198
	- clone_job: Kubernetes Job for cloning a drupal site
Dimitra Chatzichrysou's avatar
Dimitra Chatzichrysou committed
199
	- easystart_taskrun: Taskrun for restoring easystart backup
200
201
202
	- is_base: ImageStream for sitebuilder-base
	- is_s2i: ImageStream for S2I sitebuilder
	- bc_s2i: BuildConfig for S2I sitebuilder
203
	- deploy_drupal: <moved to `ensureDrupalDeployment`>
204
205
	- svc_nginx: Service for Nginx
	- cm_php: ConfigMap for PHP-FPM
206
	- cm_nginx_global: ConfigMap for Nginx global settings (performance)
207
	- cm_settings: ConfigMap for `settings.php`
208
	- cm_php_cli: ConfigMap for 'config.ini' for PHP CLI
209
	- route: Route for the drupalsite
210
	- oidc_return_uri: Redirection URI for OIDC
211
	- dbod_cr: DBOD custom resource to establish database & respective connection for the drupalsite
212
	- webdav_secret: Secret with credential for WebDAV
213
	- backup_schedule: Velero Schedule for scheduled backups of the drupalSite
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
214
	- tekton_extra_perm_rbac: ClusterRoleBinding for tekton tasks
Francisco Borges Aurindo Barros's avatar
Francisco Borges Aurindo Barros committed
215
	- gitlab_trigger_secret: Secret for Gitlab trigger config in buildconfig
216
*/
217
func (r *DrupalSiteReconciler) ensureResourceX(ctx context.Context, d *webservicesv1a1.DrupalSite, resType string, log logr.Logger) (transientErr reconcileError) {
218
219
	switch resType {
	case "is_s2i":
220
		is := &imagev1.ImageStream{ObjectMeta: metav1.ObjectMeta{Name: "sitebuilder-s2i-" + d.Name, Namespace: d.Namespace}}
221
222
223
224
225
226
227
228
229
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, is, func() error {
			return imageStreamForDrupalSiteBuilderS2I(is, d)
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", is.TypeMeta.Kind, "Resource.Namespace", is.Namespace, "Resource.Name", is.Name)
			return newApplicationError(err, ErrClientK8s)
		}
		return nil
	case "bc_s2i":
230
		bc := &buildv1.BuildConfig{ObjectMeta: metav1.ObjectMeta{Name: "sitebuilder-s2i-" + nameVersionHash(d), Namespace: d.Namespace}}
231
		// We don't really benefit from udating here, because of https://docs.openshift.com/container-platform/4.6/builds/triggering-builds-build-hooks.html#builds-configuration-change-triggers_triggering-builds-build-hooks
232
233
234
235
236
237
238
239
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, bc, func() error {
			return buildConfigForDrupalSiteBuilderS2I(bc, d)
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", bc.TypeMeta.Kind, "Resource.Namespace", bc.Namespace, "Resource.Name", bc.Name)
			return newApplicationError(err, ErrClientK8s)
		}
		return nil
240
	case "webdav_secret":
241
242
243
		// TODO: secret names must be short (I believe <64 chars), and given the maximum name length of a DrupalSite (50 chars)
		// the webdav secret is too long.
		// In order to shorten this name we'll have to change the deployment to enforce the volumes.
Dimitra Chatzichrysou's avatar
Dimitra Chatzichrysou committed
244
		webdav_secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "webdav-secret-" + d.Name, Namespace: d.Namespace}}
245
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, webdav_secret, func() error {
246
			log.V(4).Info("Ensuring Resource", "Kind", webdav_secret.TypeMeta.Kind, "Resource.Namespace", webdav_secret.Namespace, "Resource.Name", webdav_secret.Name)
247
			return secretForWebDAV(webdav_secret, d)
248
249
250
251
252
253
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", webdav_secret.TypeMeta.Kind, "Resource.Namespace", webdav_secret.Namespace, "Resource.Name", webdav_secret.Name)
			return newApplicationError(err, ErrClientK8s)
		}
		return nil
254
	case "svc_nginx":
255
		svc := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: d.Name, Namespace: d.Namespace}}
256
257
258
259
260
261
262
263
264
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, svc, func() error {
			return serviceForDrupalSite(svc, d)
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", svc.TypeMeta.Kind, "Resource.Namespace", svc.Namespace, "Resource.Name", svc.Name)
			return newApplicationError(err, ErrClientK8s)
		}
		return nil
	case "pvc_drupal":
265
		pvc := &corev1.PersistentVolumeClaim{ObjectMeta: metav1.ObjectMeta{Name: "pv-claim-" + d.Name, Namespace: d.Namespace}}
266
267
268
269
270
271
272
273
274
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, pvc, func() error {
			return persistentVolumeClaimForDrupalSite(pvc, d)
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", pvc.TypeMeta.Kind, "Resource.Namespace", pvc.Namespace, "Resource.Name", pvc.Name)
			return newApplicationError(err, ErrClientK8s)
		}
		return nil
	case "route":
275
276
		routeRequestList := d.Spec.SiteURL
		for _, req := range routeRequestList {
277
278
			hash := md5.Sum([]byte(req))
			route := &routev1.Route{ObjectMeta: metav1.ObjectMeta{Name: d.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: d.Namespace}}
279
280
281
			_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, route, func() error {
				return routeForDrupalSite(route, d, string(req))
			})
282
			// TODO: don't throw on conflict
283
284
285
286
287
			if err != nil {
				log.Error(err, "Failed to ensure Resource", "Kind", route.TypeMeta.Kind, "Resource.Namespace", route.Namespace, "Resource.Name", route.Name)
				return newApplicationError(err, ErrClientK8s)
			}
		}
288
		return nil
289
	case "oidc_return_uri":
290
291
		routeRequestList := d.Spec.SiteURL
		for _, req := range routeRequestList {
292
293
			hash := md5.Sum([]byte(req))
			OidcReturnURI := &authz.OidcReturnURI{ObjectMeta: metav1.ObjectMeta{Name: d.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: d.Namespace}}
294
			_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, OidcReturnURI, func() error {
295
				log.V(4).Info("Ensuring Resource", "Kind", OidcReturnURI.TypeMeta.Kind, "Resource.Namespace", OidcReturnURI.Namespace, "Resource.Name", OidcReturnURI.Name)
296
				return newOidcReturnURI(OidcReturnURI, d, string(req), true)
297
298
299
300
			})
			if err != nil {
				log.Error(err, "Failed to ensure Resource", "Kind", OidcReturnURI.TypeMeta.Kind, "Resource.Namespace", OidcReturnURI.Namespace, "Resource.Name", OidcReturnURI.Name)
			}
301
302
			OidcReturnURIHTTPS := &authz.OidcReturnURI{ObjectMeta: metav1.ObjectMeta{Name: d.Name + "-https-" + hex.EncodeToString(hash[0:4]), Namespace: d.Namespace}}
			_, err = controllerruntime.CreateOrUpdate(ctx, r.Client, OidcReturnURIHTTPS, func() error {
303
				log.V(4).Info("Ensuring Resource", "Kind", OidcReturnURIHTTPS.TypeMeta.Kind, "Resource.Namespace", OidcReturnURIHTTPS.Namespace, "Resource.Name", OidcReturnURIHTTPS.Name)
304
305
306
307
308
				return newOidcReturnURI(OidcReturnURIHTTPS, d, string(req), false)
			})
			if err != nil {
				log.Error(err, "Failed to ensure Resource", "Kind", OidcReturnURI.TypeMeta.Kind, "Resource.Namespace", OidcReturnURIHTTPS.Namespace, "Resource.Name", OidcReturnURIHTTPS.Name)
			}
309
		}
310
		return nil
311
	case "site_install_job":
312
313
		databaseSecretName := databaseSecretName(d)
		if len(databaseSecretName) == 0 {
314
315
			return nil
		}
316
317
		// TODO: this name is too long
		// change to `install-*`
318
		job := &batchv1.Job{ObjectMeta: metav1.ObjectMeta{Name: "ensure-site-install-" + d.Name, Namespace: d.Namespace}}
319
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, job, func() error {
320
			return jobForDrupalSiteInstallation(job, databaseSecretName, d)
321
322
323
324
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", job.TypeMeta.Kind, "Resource.Namespace", job.Namespace, "Resource.Name", job.Name)
			return newApplicationError(err, ErrClientK8s)
325
326
		}
		return nil
Dimitra Chatzichrysou's avatar
Dimitra Chatzichrysou committed
327
	case "clone_job":
328
		if databaseSecret := databaseSecretName(d); len(databaseSecret) != 0 {
Dimitra Chatzichrysou's avatar
Dimitra Chatzichrysou committed
329
330
			job := &batchv1.Job{ObjectMeta: metav1.ObjectMeta{Name: "clone-" + d.Name, Namespace: d.Namespace}}
			_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, job, func() error {
331
				log.V(4).Info("Ensuring Resource", "Kind", job.TypeMeta.Kind, "Resource.Namespace", job.Namespace, "Resource.Name", job.Name)
332
				return jobForDrupalSiteClone(job, databaseSecret, d)
Dimitra Chatzichrysou's avatar
Dimitra Chatzichrysou committed
333
334
335
336
			})
			if err != nil {
				log.Error(err, "Failed to ensure Resource", "Kind", job.TypeMeta.Kind, "Resource.Namespace", job.Namespace, "Resource.Name", job.Name)
				return newApplicationError(err, ErrClientK8s)
Dimitra Chatzichrysou's avatar
Dimitra Chatzichrysou committed
337
338
339
			}
		}
		return nil
340
341
342
343
344
	case "easystart_taskrun":
		if databaseSecret := databaseSecretName(d); len(databaseSecret) != 0 {
			taskRun := &pipelinev1.TaskRun{
				ObjectMeta: metav1.ObjectMeta{Name: "easystart-" + d.Name, Namespace: d.Namespace}}
			_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, taskRun, func() error {
345
				log.V(4).Info("Ensuring Resource", "Kind", taskRun.TypeMeta.Kind, "Resource.Namespace", taskRun.Namespace, "Resource.Name", taskRun.Name)
346
347
348
349
350
351
352
353
				return taskRunForEasystartRestore(taskRun, d)
			})
			if err != nil {
				log.Error(err, "Failed to ensure Resource", "Kind", taskRun.TypeMeta.Kind, "Resource.Namespace", taskRun.Namespace, "Resource.Name", taskRun.Name)
				return newApplicationError(err, ErrClientK8s)
			}
		}
		return nil
354
	case "cm_php":
355
		cm := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "php-fpm-" + d.Name, Namespace: d.Namespace}}
Konstantinos Samaras-Tsakiris's avatar
Konstantinos Samaras-Tsakiris committed
356
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, cm, func() error {
357
358
359
360
361
362
363
			return updateConfigMapForPHPFPM(ctx, cm, d, r.Client)
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", cm.TypeMeta.Kind, "Resource.Namespace", cm.Namespace, "Resource.Name", cm.Name)
			return newApplicationError(err, ErrClientK8s)
		}
		return nil
364
	case "cm_nginx_global":
Konstantinos Samaras-Tsakiris's avatar
Konstantinos Samaras-Tsakiris committed
365
		cm := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "nginx-global-" + d.Name, Namespace: d.Namespace}}
366
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, cm, func() error {
367
			return updateConfigMapForNginxGlobal(ctx, cm, d, r.Client)
368
369
370
371
372
373
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", cm.TypeMeta.Kind, "Resource.Namespace", cm.Namespace, "Resource.Name", cm.Name)
			return newApplicationError(err, ErrClientK8s)
		}
		return nil
374
	case "cm_settings":
375
376
		// TODO: configmap names must be short (I believe <64 chars), and given the maximum name length of a DrupalSite (50 chars), this is too long
		// In order to shorten this name we'll have to change the deployment to enforce the volumes.
377
378
		cm := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "site-settings-" + d.Name, Namespace: d.Namespace}}
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, cm, func() error {
379
			return updateConfigMapForSiteSettings(ctx, cm, d, r.Client)
380
381
382
383
384
385
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", cm.TypeMeta.Kind, "Resource.Namespace", cm.Namespace, "Resource.Name", cm.Name)
			return newApplicationError(err, ErrClientK8s)
		}
		return nil
386
	case "cm_php_cli":
387
388
		// TODO: configmap names must be short (I believe <64 chars), and given the maximum name length of a DrupalSite (50 chars), this is too long
		// In order to shorten this name we'll have to change the deployment to enforce the volumes.
389
		cm := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "php-cli-config-" + d.Name, Namespace: d.Namespace}}
390
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, cm, func() error {
391
			return updateConfigMapForPHPCLI(ctx, cm, d, r.Client)
392
393
394
395
396
397
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", cm.TypeMeta.Kind, "Resource.Namespace", cm.Namespace, "Resource.Name", cm.Name)
			return newApplicationError(err, ErrClientK8s)
		}
		return nil
398
	case "dbod_cr":
399
		dbod := &dbodv1a1.Database{ObjectMeta: metav1.ObjectMeta{Name: d.Name, Namespace: d.Namespace}}
400
401
402
403
404
405
406
407
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, dbod, func() error {
			return dbodForDrupalSite(dbod, d)
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", dbod.TypeMeta.Kind, "Resource.Namespace", dbod.Namespace, "Resource.Name", dbod.Name)
			return newApplicationError(err, ErrClientK8s)
		}
		return nil
408
	case "backup_schedule":
409
		schedule := &velerov1.Schedule{ObjectMeta: metav1.ObjectMeta{Name: generateScheduleName(d.Namespace, d.Name), Namespace: VeleroNamespace}}
410
411
412
413
414
415
416
417
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, schedule, func() error {
			return scheduledBackupsForDrupalSite(schedule, d)
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", schedule.TypeMeta.Kind, "Resource.Namespace", schedule.Namespace, "Resource.Name", schedule.Name)
			return newApplicationError(err, ErrClientK8s)
		}
		return nil
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
418
419
420
421
422
423
424
425
426
	case "tekton_extra_perm_rbac":
		// We only need one ClusterRoleBinding for a given project. Therefore the naming. It gets created by any of the sites in
		// the project if it doesn't exist. We don't delete it specifically as well, it can be handled with project deletion
		rbac := &rbacv1.ClusterRoleBinding{ObjectMeta: metav1.ObjectMeta{Name: "tektoncd-extra-permissions-" + d.Namespace}}
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, rbac, func() error {
			return clusterRoleBindingForTektonExtraPermission(rbac, d)
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", rbac.TypeMeta.Kind, "Resource.Name", rbac.Name)
427
428
		}
		return nil
Francisco Borges Aurindo Barros's avatar
Francisco Borges Aurindo Barros committed
429
	case "gitlab_trigger_secret":
430
431
		// TODO: secret names must be short (I believe <64 chars), and given the maximum name length of a DrupalSite (50 chars), this is too long
		// In order to shorten this name we'll have to change the deployment to enforce the volumes.
Francisco Borges Aurindo Barros's avatar
Francisco Borges Aurindo Barros committed
432
433
		gitlab_trigger_secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "gitlab-trigger-secret-" + d.Name, Namespace: d.Namespace}}
		_, err := controllerruntime.CreateOrUpdate(ctx, r.Client, gitlab_trigger_secret, func() error {
434
			log.V(4).Info("Ensuring Resource", "Kind", gitlab_trigger_secret.TypeMeta.Kind, "Resource.Namespace", gitlab_trigger_secret.Namespace, "Resource.Name", gitlab_trigger_secret.Name)
Francisco Borges Aurindo Barros's avatar
Francisco Borges Aurindo Barros committed
435
436
437
438
439
440
441
			return secretForS2iGitlabTrigger(gitlab_trigger_secret, d)
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", gitlab_trigger_secret.TypeMeta.Kind, "Resource.Namespace", gitlab_trigger_secret.Namespace, "Resource.Name", gitlab_trigger_secret.Name)
			return newApplicationError(err, ErrClientK8s)
		}
		return nil
442
443
444
445
446
	default:
		return newApplicationError(nil, ErrFunctionDomain)
	}
}

447
448
449
/*
ensureDrupalDeployment is similar to ensureResourceX, but for the Drupal server deployment, which requires extra information.
*/
450
func (r *DrupalSiteReconciler) ensureDrupalDeployment(ctx context.Context, d *webservicesv1a1.DrupalSite, config DeploymentConfig, log logr.Logger) (transientErr reconcileError) {
451
452
453
454
455
	deploy := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: d.Name, Namespace: d.Namespace}}
	err := r.Get(ctx, types.NamespacedName{Name: deploy.Name, Namespace: deploy.Namespace}, deploy)

	// Check if a deployment exists & if any of the given conditions satisfy
	// In scenarios where, the deployment is deleted during a failed upgrade, this check is needed to bring it back
456
	if err == nil && (d.ConditionTrue("CodeUpdateFailed") || d.ConditionTrue("DBUpdatesFailed")) {
457
458
459
460
461
462
		return nil
	}
	if databaseSecret := databaseSecretName(d); len(databaseSecret) != 0 {
		deploy := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: d.Name, Namespace: d.Namespace}}
		_, err = controllerruntime.CreateOrUpdate(ctx, r.Client, deploy, func() error {
			releaseID := releaseID(d)
463
			return deploymentForDrupalSite(deploy, databaseSecret, d, releaseID, config)
464
465
466
467
468
469
470
471
472
		})
		if err != nil {
			log.Error(err, "Failed to ensure Resource", "Kind", deploy.TypeMeta.Kind, "Resource.Namespace", deploy.Namespace, "Resource.Name", deploy.Name)
			return newApplicationError(err, ErrClientK8s)
		}
	}
	return nil
}

473
474
475
476
477
// ensureNoExtraRouteResource uses the current SiteURL resource as reference and deletes any extra route
func (r *DrupalSiteReconciler) ensureNoExtraRouteResource(ctx context.Context, d *webservicesv1a1.DrupalSite, label string, log logr.Logger) (transientErr reconcileError) {
	ls := labelsForDrupalSite(d.Name)
	ls["app"] = "drupal"
	ls["route"] = label
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
478
	existingRoutes := &routev1.RouteList{}
479
480
481
482
483
484
485
486
487
488
	routeLabels, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{
		MatchLabels: ls,
	})
	if err != nil {
		return newApplicationError(err, ErrFunctionDomain)
	}
	options := client.ListOptions{
		Namespace:     d.Namespace,
		LabelSelector: routeLabels,
	}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
489
	err = r.Client.List(context.TODO(), existingRoutes, &options)
490
	if err != nil {
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
491
		log.Error(err, "Couldn't query routes with the given labels")
492
493
494
		return newApplicationError(err, ErrClientK8s)
	}
	routeRequestList := d.Spec.SiteURL
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
495
496
497
498
499
500
501
	routesToRemove := []webservicesv1a1.Url{}
	for _, route := range existingRoutes.Items {
		flag := false
		for _, req := range routeRequestList {
			if label == "webdav" {
				req = "webdav-" + req
			}
502
			if string(req) == route.Spec.Host {
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
503
504
				flag = true
				continue
505
506
			}
		}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
507
508
509
		if !flag {
			routesToRemove = append(routesToRemove, webservicesv1a1.Url(route.Spec.Host))
		}
510
	}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
511
	for _, route := range routesToRemove {
512
513
		if transientErr := r.ensureNoRoute(ctx, d, string(route), log); transientErr != nil {
			return transientErr
514
515
516
517
518
519
520
521
522
523
		}
	}
	return nil
}

// ensureNoExtraOidcReturnUriResource uses the current SiteURL resource as reference and deletes any extra oidcReturnURI
func (r *DrupalSiteReconciler) ensureNoExtraOidcReturnUriResource(ctx context.Context, d *webservicesv1a1.DrupalSite, label string, log logr.Logger) (transientErr reconcileError) {
	ls := labelsForDrupalSite(d.Name)
	ls["app"] = "drupal"
	ls["oidcReturnURI"] = label
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
524
525
	existingOidcReturnUris := &authz.OidcReturnURIList{}
	oidcReturnUriLabels, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{
526
527
528
529
530
531
532
		MatchLabels: ls,
	})
	if err != nil {
		return newApplicationError(err, ErrFunctionDomain)
	}
	options := client.ListOptions{
		Namespace:     d.Namespace,
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
533
		LabelSelector: oidcReturnUriLabels,
534
	}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
535
	err = r.Client.List(context.TODO(), existingOidcReturnUris, &options)
536
	if err != nil {
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
537
		log.Error(err, "Couldn't query oidcReturnUris with the given labels")
538
539
		return newApplicationError(err, ErrClientK8s)
	}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
540
541
542
543
544
	oidcReturnUriRequestList := d.Spec.SiteURL
	oidcReturnUrisToRemove := []string{}
	for _, route := range existingOidcReturnUris.Items {
		flag := false
		for _, req := range oidcReturnUriRequestList {
545
546
547
548
549
			url, err := url.Parse(route.Spec.RedirectURI)
			if err != nil {
				return newApplicationError(err, ErrFunctionDomain)
			}
			if string(req) == url.Host {
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
550
551
				flag = true
				continue
552
553
			}
		}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
554
555
556
557
558
559
560
		if !flag {
			url, err := url.Parse(route.Spec.RedirectURI)
			if err != nil {
				return newApplicationError(err, ErrFunctionDomain)
			}
			oidcReturnUrisToRemove = append(oidcReturnUrisToRemove, url.Host)
		}
561
	}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
562
563
	for _, oidcReturnURI := range oidcReturnUrisToRemove {
		if transientErr := r.ensureNoReturnURI(ctx, d, oidcReturnURI, log); transientErr != nil {
564
565
566
567
568
569
			return transientErr
		}
	}
	return nil
}

Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
570
// ensureNoRoute ensures there is no route object for the drupalsite
571
func (r *DrupalSiteReconciler) ensureNoRoute(ctx context.Context, d *webservicesv1a1.DrupalSite, Url string, log logr.Logger) (transientErr reconcileError) {
572
573
	hash := md5.Sum([]byte(Url))
	route := &routev1.Route{ObjectMeta: metav1.ObjectMeta{Name: d.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: d.Namespace}}
574
575
576
577
578
579
580
581
582
583
584
585
586
587
	if err := r.Get(ctx, types.NamespacedName{Name: route.Name, Namespace: route.Namespace}, route); err != nil {
		switch {
		case k8sapierrors.IsNotFound(err):
			return nil
		default:
			return newApplicationError(err, ErrClientK8s)
		}
	}
	if err := r.Delete(ctx, route); err != nil {
		return newApplicationError(err, ErrClientK8s)
	}
	return nil
}

Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
588
// ensureNoReturnURI ensures there is no OIDC Return URI object for the drupalsite
589
func (r *DrupalSiteReconciler) ensureNoReturnURI(ctx context.Context, d *webservicesv1a1.DrupalSite, Url string, log logr.Logger) (transientErr reconcileError) {
590
	hash := md5.Sum([]byte(Url))
591
	oidc_return_uri := &authz.OidcReturnURI{}
592
	if err := r.Get(ctx, types.NamespacedName{Name: d.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: d.Namespace}, oidc_return_uri); err != nil {
593
594
595
596
597
598
599
600
601
602
603
604
605
		switch {
		case k8sapierrors.IsNotFound(err):
			return nil
		default:
			return newApplicationError(err, ErrClientK8s)
		}
	}
	if err := r.Delete(ctx, oidc_return_uri); err != nil {
		return newApplicationError(err, ErrClientK8s)
	}
	return nil
}

606
// ensureNoBackupSchedule ensures there is no Schedule object for the drupalsite
607
608
func (r *DrupalSiteReconciler) ensureNoBackupSchedule(ctx context.Context, d *webservicesv1a1.DrupalSite, log logr.Logger) (transientErr reconcileError) {
	schedule := &velerov1.Schedule{}
609
	if err := r.Get(ctx, types.NamespacedName{Name: generateScheduleName(d.Namespace, d.Name), Namespace: VeleroNamespace}, schedule); err != nil {
610
611
612
613
614
615
616
		switch {
		case k8sapierrors.IsNotFound(err):
			return nil
		default:
			return newApplicationError(err, ErrClientK8s)
		}
	}
617
	if err := r.Delete(ctx, schedule); err != nil {
618
619
620
621
622
		return newApplicationError(err, ErrClientK8s)
	}
	return nil
}

Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
623
// imageStreamForDrupalSiteBuilderS2I returns a ImageStream object for Drupal SiteBuilder S2I
624
func imageStreamForDrupalSiteBuilderS2I(currentobject *imagev1.ImageStream, d *webservicesv1a1.DrupalSite) error {
625
626
	addOwnerRefToObject(currentobject, asOwner(d))
	currentobject.Spec.LookupPolicy.Local = true
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
627
628
629
630
	if currentobject.Labels == nil {
		currentobject.Labels = map[string]string{}
	}
	ls := labelsForDrupalSite(d.Name)
631
	ls["app"] = "sitebuilder"
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
632
633
634
	for k, v := range ls {
		currentobject.Labels[k] = v
	}
635
	return nil
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
636
637
638
}

// buildConfigForDrupalSiteBuilderS2I returns a BuildConfig object for Drupal SiteBuilder S2I
639
func buildConfigForDrupalSiteBuilderS2I(currentobject *buildv1.BuildConfig, d *webservicesv1a1.DrupalSite) error {
640
	addOwnerRefToObject(currentobject, asOwner(d))
641
	if currentobject.CreationTimestamp.IsZero() {
642
643
644
645
646
647
648
649
		currentobject.Spec = buildv1.BuildConfigSpec{
			CommonSpec: buildv1.CommonSpec{
				Resources:                 BuildResources,
				CompletionDeadlineSeconds: pointer.Int64Ptr(1200),
				Strategy: buildv1.BuildStrategy{
					SourceStrategy: &buildv1.SourceBuildStrategy{
						From: corev1.ObjectReference{
							Kind: "DockerImage",
650
							Name: SiteBuilderImage + ":" + releaseID(d),
651
						},
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
652
653
					},
				},
654
655
656
				Output: buildv1.BuildOutput{
					To: &corev1.ObjectReference{
						Kind: "ImageStreamTag",
657
						Name: "sitebuilder-s2i-" + d.Name + ":" + releaseID(d),
658
					},
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
659
660
				},
			},
661
662
663
664
			Triggers: []buildv1.BuildTriggerPolicy{
				{
					Type: buildv1.ConfigChangeBuildTriggerType,
				},
665
666
667
668
669
670
				{
					Type: buildv1.GitLabWebHookBuildTriggerType,
					GitLabWebHook: &buildv1.WebHookTrigger{
						Secret: "gitlab-trigger-secret-" + d.Name,
					},
				},
671
			},
672
		}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
673
	}
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
	// TODO: Remove logic for ExtraConfigurationRepo once we deprecate the field
	if currentobject.CreationTimestamp.IsZero() && len(d.Spec.Configuration.ExtraConfigurationRepo) > 0 {
		currentobject.Spec.CommonSpec.Source = buildv1.BuildSource{
			Git: &buildv1.GitBuildSource{
				Ref: "master",
				URI: d.Spec.Configuration.ExtraConfigurationRepo,
			},
		}
	} else if currentobject.CreationTimestamp.IsZero() && len(d.Spec.Configuration.ExtraConfigurationRepository.RepositoryUrl) > 0 {
		currentobject.Spec.CommonSpec.Source = buildv1.BuildSource{
			Git: &buildv1.GitBuildSource{
				Ref: d.Spec.Configuration.ExtraConfigurationRepository.Branch,
				URI: string(d.Spec.Configuration.ExtraConfigurationRepository.RepositoryUrl),
			},
		}
	}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
690
691
692
693
	if currentobject.Labels == nil {
		currentobject.Labels = map[string]string{}
	}
	ls := labelsForDrupalSite(d.Name)
694
	ls["app"] = "sitebuilder"
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
695
696
697
	for k, v := range ls {
		currentobject.Labels[k] = v
	}
698
	return nil
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
699
700
}

701
// dbodForDrupalSite returns a DBOD resource for the the Drupal Site
702
func dbodForDrupalSite(currentobject *dbodv1a1.Database, d *webservicesv1a1.DrupalSite) error {
703
	addOwnerRefToObject(currentobject, asOwner(d))
704
	if currentobject.CreationTimestamp.IsZero() {
705
706
		dbID := md5.Sum([]byte(d.Namespace + "-" + d.Name))
		currentobject.Spec = dbodv1a1.DatabaseSpec{
707
			DatabaseClass: string(d.Spec.Configuration.DatabaseClass),
708
709
710
711
712
713
			DbName:        hex.EncodeToString(dbID[1:10]),
			DbUser:        hex.EncodeToString(dbID[1:10]),
			ExtraLabels: map[string]string{
				"drupalSite": d.Name,
			},
		}
714
	}
715
	// Enforce only the drupalsite labels on the resource on every iteration
716
717
718
	if currentobject.Labels == nil {
		currentobject.Labels = map[string]string{}
	}
719
	ls := labelsForDrupalSite(d.Name)
720
	ls["app"] = "dbod"
721
722
723
724
	for k, v := range ls {
		currentobject.Labels[k] = v
	}
	return nil
725
726
}

727
// deploymentForDrupalSite defines the server runtime deployment of a DrupalSite
728
func deploymentForDrupalSite(currentobject *appsv1.Deployment, databaseSecret string, d *webservicesv1a1.DrupalSite, releaseID string, config DeploymentConfig) error {
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
729
730
731
732
733
734
735
736
737
	ls := labelsForDrupalSite(d.Name)
	if currentobject.Labels == nil {
		currentobject.Labels = map[string]string{}
	}
	ls["app"] = "drupal"
	for k, v := range ls {
		currentobject.Labels[k] = v
	}

738
	addOwnerRefToObject(currentobject, asOwner(d))
739
	if currentobject.Annotations == nil {
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
740
		currentobject.Annotations = map[string]string{}
741
742
	}
	currentobject.Annotations["alpha.image.policy.openshift.io/resolve-names"] = "*"
743

744
745
746
747
748
749
750
751
752
753
754
	if currentobject.CreationTimestamp.IsZero() {
		currentobject.Spec.Template.Spec.Containers = []corev1.Container{{Name: "nginx"}, {Name: "php-fpm"}, {Name: "php-fpm-exporter"}, {Name: "webdav"}, {Name: "cron"}, {Name: "drupal-logs"}}
	} else {
		containerExists("nginx", currentobject)
		containerExists("php-fpm", currentobject)
		containerExists("php-fpm-exporter", currentobject)
		containerExists("webdav", currentobject)
		containerExists("cron", currentobject)
		containerExists("drupal-logs", currentobject)
	}

755
	// Settings only on creation (not enforced)
756
	if currentobject.CreationTimestamp.IsZero() {
757
		currentobject.Spec.Template.ObjectMeta.Annotations = map[string]string{}
758
759
760
761
		currentobject.Spec.Selector = &metav1.LabelSelector{
			MatchLabels: ls,
		}
		currentobject.Spec.Template.ObjectMeta.Labels = ls
762

763
764
765
766
767
		if _, bool := d.Annotations["nodeSelectorLabel"]; bool {
			if _, bool = d.Annotations["nodeSelectorValue"]; bool {
				currentobject.Spec.Template.Spec.NodeSelector = map[string]string{
					d.Annotations["nodeSelectorLabel"]: d.Annotations["nodeSelectorValue"],
				}
768
769
770
			}
		}

771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
		currentobject.Spec.Template.Spec.Volumes = []corev1.Volume{
			{
				Name: "drupal-directory-" + d.Name,
				VolumeSource: corev1.VolumeSource{
					PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
						ClaimName: "pv-claim-" + d.Name,
					},
				}},
			{
				Name: "php-config-volume",
				VolumeSource: corev1.VolumeSource{
					ConfigMap: &corev1.ConfigMapVolumeSource{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: "php-fpm-" + d.Name,
						},
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
786
					},
787
788
				},
			},
789
			{
790
				Name: "nginx-global-config",
791
792
793
				VolumeSource: corev1.VolumeSource{
					ConfigMap: &corev1.ConfigMapVolumeSource{
						LocalObjectReference: corev1.LocalObjectReference{
794
							Name: "nginx-global-" + d.Name,
795
						},
796
797
					},
				},
798
			},
799
800
801
802
803
804
805
			{
				Name: "site-settings-php",
				VolumeSource: corev1.VolumeSource{
					ConfigMap: &corev1.ConfigMapVolumeSource{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: "site-settings-" + d.Name,
						},
806
807
808
					},
				},
			},
809
810
811
812
813
814
815
816
817
			{
				Name:         "empty-dir",
				VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
			},
			{
				Name: "webdav-volume",
				VolumeSource: corev1.VolumeSource{
					Secret: &corev1.SecretVolumeSource{
						SecretName: "webdav-secret-" + d.Name,
818
819
820
821
822
823
824
						Items: []corev1.KeyToPath{
							// Unecessary but garantees no other secrets are mounted
							{
								Key:  "htdigest",
								Path: "htdigest",
							},
						},
825
					},
826
827
				},
			},
828
829
830
831
832
833
834
			{
				// Tmp Dir storage to address issue https://gitlab.cern.ch/webservices/webframeworks-planning/-/issues/600
				Name: "tmp-dir",
				VolumeSource: corev1.VolumeSource{
					EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory},
				},
			},
835
836
837
838
839
840
841
842
843
844
			{
				Name: "php-cli-config-volume",
				VolumeSource: corev1.VolumeSource{
					ConfigMap: &corev1.ConfigMapVolumeSource{
						LocalObjectReference: corev1.LocalObjectReference{
							Name: "php-cli-config-" + d.Name,
						},
					},
				},
			},
845
		}
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
846

847
		// TODO: gradually migrate this code outside of the `CreationTimestamp.IsZero` check
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
		for i, container := range currentobject.Spec.Template.Spec.Containers {
			switch container.Name {
			case "nginx":
				// Set to always due to https://gitlab.cern.ch/drupal/paas/drupalsite-operator/-/issues/54
				currentobject.Spec.Template.Spec.Containers[i].ImagePullPolicy = "Always"
				currentobject.Spec.Template.Spec.Containers[i].Ports = []corev1.ContainerPort{{
					ContainerPort: 8080,
					Name:          "nginx",
					Protocol:      "TCP",
				}}
				currentobject.Spec.Template.Spec.Containers[i].Env = []corev1.EnvVar{
					{
						Name:  "DRUPAL_SHARED_VOLUME",
						Value: "/drupal-data",
					},
				}
				currentobject.Spec.Template.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{
					{
						Name:      "drupal-directory-" + d.Name,
						MountPath: "/drupal-data",
					},
					{
870
871
872
						Name:      "nginx-global-config",
						MountPath: "/etc/nginx/global.conf",
						SubPath:   "global.conf",
873
874
875
876
877
878
879
						ReadOnly:  true,
					},
					{
						Name:      "empty-dir",
						MountPath: "/var/run/",
					},
				}
880
				// TODO: add readiness probe. Tmp removed due to https://gitlab.cern.ch/webservices/webframeworks-planning/-/issues/542
881
882
883
884
885
886
887
888
889
890
891
892
893
			case "php-fpm":
				// Set to always due to https://gitlab.cern.ch/drupal/paas/drupalsite-operator/-/issues/54
				currentobject.Spec.Template.Spec.Containers[i].ImagePullPolicy = "Always"
				currentobject.Spec.Template.Spec.Containers[i].Ports = []corev1.ContainerPort{{
					ContainerPort: 9000,
					Name:          "php-fpm",
					Protocol:      "TCP",
				}}
				currentobject.Spec.Template.Spec.Containers[i].Env = []corev1.EnvVar{
					{
						Name:  "DRUPAL_SHARED_VOLUME",
						Value: "/drupal-data",
					},
894
895
896
897
					{
						Name:  "SMTPHOST",
						Value: SMTPHost,
					},
898
899
900
901
902
903
904
				}
				currentobject.Spec.Template.Spec.Containers[i].EnvFrom = []corev1.EnvFromSource{
					{
						SecretRef: &corev1.SecretEnvSource{
							LocalObjectReference: corev1.LocalObjectReference{
								Name: databaseSecret,
							},
Vineet Reddy Rajula's avatar
Vineet Reddy Rajula committed
905
906
						},
					},
907
908
909
910
911
					{
						SecretRef: &corev1.SecretEnvSource{
							LocalObjectReference: corev1.LocalObjectReference{
								Name: oidcSecretName, //This is always set the same way
							},
912
913
						},
					},
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
				}
				currentobject.Spec.Template.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{
					{
						Name:      "drupal-directory-" + d.Name,
						MountPath: "/drupal-data",
					},
					{
						Name:      "php-config-volume",
						MountPath: "/usr/local/etc/php-fpm.d/zz-docker.conf",
						SubPath:   "zz-docker.conf",
						ReadOnly:  true,
					},
					{
						Name:      "empty-dir",
						MountPath: "/var/run/",
					},
					{
						Name:      "site-settings-php",
						MountPath: "/app/web/sites/default/settings.php",
						SubPath:   "settings.php",
						ReadOnly:  true,
					},
936
937
938
939
940
					{
						// Tmp Dir storage to address issue https://gitlab.cern.ch/webservices/webframeworks-planning/-/issues/600
						Name:      "tmp-dir",
						MountPath: "/tmp",
					},
941
942
943
944
945
946
					{
						Name:      "php-cli-config-volume",
						MountPath: "/usr/local/etc/php/conf.d/config.ini",
						SubPath:   "config.ini",
						ReadOnly:  true,
					},
947
				}
948
			case "php-fpm-exporter":
949
950
				// Set to always due to https://gitlab.cern.ch/drupal/paas/drupalsite-operator/-/issues/54
				currentobject.Spec.Template.Spec.Containers[i].ImagePullPolicy = "Always"
951
				// Port on which to expose metrics
952
				currentobject.Spec.Template.Spec.Containers[i].Ports = []corev1.ContainerPort{{
953
954
					ContainerPort: 9253,
					Name:          "php-fpm-metrics",
955
956
					Protocol:      "TCP",
				}}
957
958
959
960
961
962
				currentobject.Spec.Template.Spec.Containers[i].Env = []corev1.EnvVar{
					{
						Name:  "PHP_FPM_SCRAPE_URI",
						Value: "unix:///var/run/drupal.sock;/_site/_php-fpm-status",
					},
				}
963
964
				currentobject.Spec.Template.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{
					{