mirror of
https://forge.liiib.re/indiehost/libre.sh/libre.sh.git
synced 2024-12-28 14:46:41 +00:00
feat: add decidim app
This commit is contained in:
parent
3878edd39f
commit
293e2be821
33 changed files with 2552 additions and 6 deletions
9
PROJECT
9
PROJECT
|
@ -117,4 +117,13 @@ resources:
|
|||
kind: Forgejo
|
||||
path: libre.sh/api/apps/v1alpha1
|
||||
version: v1alpha1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: true
|
||||
controller: true
|
||||
domain: libre.sh
|
||||
group: apps
|
||||
kind: Decidim
|
||||
path: libre.sh/api/apps/v1alpha1
|
||||
version: v1alpha1
|
||||
version: "3"
|
||||
|
|
141
api/apps/v1alpha1/decidim_types.go
Normal file
141
api/apps/v1alpha1/decidim_types.go
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
Copyright 2024 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
lshmeta "libre.sh/api/meta/v1alpha1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
const DecidimConfigSuffix = "decidim.libre.sh"
|
||||
|
||||
type DecidimLocale struct {
|
||||
//+kubebuilder:validation:Optional
|
||||
//+kubebuilder:default="fr"
|
||||
Default string `json:"default"`
|
||||
//+kubebuilder:validation:Optional
|
||||
//+kubebuilder:validation:MinItems=1
|
||||
//+kubebuilder:default={"fr"}
|
||||
Available []string `json:"available"`
|
||||
}
|
||||
|
||||
type DecidimFileUpload struct {
|
||||
//+kubebuilder:validation:Optional
|
||||
//+kubebuilder:default="en"
|
||||
Default string `json:"default"`
|
||||
//+kubebuilder:validation:Optional
|
||||
//+kubebuilder:validation:MinItems=1
|
||||
//+kubebuilder:default={"en","fr"}
|
||||
Available []string `json:"available"`
|
||||
}
|
||||
|
||||
type DecidimAdmin struct {
|
||||
//+kubebuilder:validation:Required
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type DecidimOrganization struct {
|
||||
//+kubebuilder:validation:Optional
|
||||
//+kubebuilder:default=1
|
||||
ID int `json:"id"`
|
||||
//+kubebuilder:validation:Optional
|
||||
Admin DecidimOrganizationAdmin `json:"admin,omitempty"`
|
||||
}
|
||||
|
||||
type DecidimOrganizationAdmin struct {
|
||||
//+kubebuilder:validation:Required
|
||||
Email string `json:"email"`
|
||||
//+kubebuilder:validation:Required
|
||||
Name string `json:"name"`
|
||||
//+kubebuilder:validation:Required
|
||||
Nickname string `json:"nickname"`
|
||||
}
|
||||
|
||||
// DecidimSpec defines the desired state of Decidim
|
||||
type DecidimSpec struct {
|
||||
lshmeta.Spec `json:",inline"`
|
||||
//+kubebuilder:validation:Required
|
||||
Image string `json:"image"`
|
||||
//+kubebuilder:validation:Optional
|
||||
Admin DecidimAdmin `json:"admin,omitempty"`
|
||||
//+kubebuilder:validation:Required
|
||||
Organization DecidimOrganization `json:"organization"`
|
||||
//+kubebuilder:validation:Required
|
||||
//+kubebuilder:validation:MinLength=3
|
||||
Host string `json:"host"`
|
||||
//+kubebuilder:validation:Optional
|
||||
AdditionalHosts []string `json:"additionalHosts,omitempty"`
|
||||
//+kubebuilder:validation:Optional
|
||||
UsersRegistrationMode int `json:"usersRegistrationMode,omitempty"`
|
||||
//+kubebuilder:validation:Optional
|
||||
ForceUsersToAuthenticateBeforeAccessOrganization bool `json:"forceUsersToAuthenticateBeforeAccessOrganization,omitempty"`
|
||||
//+kubebuilder:validation:Optional
|
||||
AvailableAuthorizations []string `json:"availableAuthorizations,omitempty"`
|
||||
//+kubebuilder:validation:Optional
|
||||
FileUploadSettings runtime.RawExtension `json:"fileUploadSettings,omitempty"`
|
||||
//+kubebuilder:validation:Required
|
||||
Locale DecidimLocale `json:"locale"`
|
||||
//+kubebuilder:validation:Optional
|
||||
//+kubebuilder:default="UTC"
|
||||
TimeZone string `json:"timeZone,omitempty"`
|
||||
// OmniAuth DecidimOmniAuth `json:"omniauth,omitempty"`
|
||||
//+kubebuilder:validation:Optional
|
||||
EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty" protobuf:"bytes,19,rep,name=envFrom"`
|
||||
}
|
||||
|
||||
// DecidimStatus defines the observed state of Decidim
|
||||
type DecidimStatus struct {
|
||||
lshmeta.Status `json:",inline"`
|
||||
}
|
||||
|
||||
//go:generate lsh-gen v1alpha1 Decidim
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
//+kubebuilder:subresource:status
|
||||
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
//+kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||
//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
|
||||
//+kubebuilder:printcolumn:name="Version",type="string",JSONPath=".status.version",description=""
|
||||
//+kubebuilder:printcolumn:name="Suspended",type="boolean",JSONPath=".spec.suspend",description=""
|
||||
|
||||
// Decidim is the Schema for the decidims API
|
||||
type Decidim struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec DecidimSpec `json:"spec,omitempty"`
|
||||
Status DecidimStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
|
||||
// DecidimList contains a list of Decidim
|
||||
type DecidimList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []Decidim `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Decidim{}, &DecidimList{})
|
||||
}
|
35
api/apps/v1alpha1/zz_generated.decidim_lsh.go
Normal file
35
api/apps/v1alpha1/zz_generated.decidim_lsh.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Code generated by lsh-gen; DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (o *Decidim) GetSuspend() bool {
|
||||
return o.Spec.Suspend
|
||||
}
|
||||
|
||||
func (o *Decidim) SetSuspend(value bool) {
|
||||
o.Spec.Suspend = value
|
||||
}
|
||||
|
||||
func (o *Decidim) GetVersion() string {
|
||||
return o.Status.Version
|
||||
}
|
||||
|
||||
func (o *Decidim) SetVersion(value string) {
|
||||
o.Status.Version = value
|
||||
}
|
||||
|
||||
func (o *Decidim) GetImage() string {
|
||||
return o.Spec.Image
|
||||
}
|
||||
|
||||
func (o *Decidim) GetConditions() []metav1.Condition {
|
||||
return o.Status.Conditions
|
||||
}
|
||||
|
||||
func (o *Decidim) SetConditions(conditions []metav1.Condition) {
|
||||
o.Status.Conditions = conditions
|
||||
}
|
|
@ -23,9 +23,207 @@ package v1alpha1
|
|||
import (
|
||||
"encoding/json"
|
||||
"k8s.io/api/core/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Decidim) DeepCopyInto(out *Decidim) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Decidim.
|
||||
func (in *Decidim) DeepCopy() *Decidim {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Decidim)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Decidim) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DecidimAdmin) DeepCopyInto(out *DecidimAdmin) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DecidimAdmin.
|
||||
func (in *DecidimAdmin) DeepCopy() *DecidimAdmin {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DecidimAdmin)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DecidimFileUpload) DeepCopyInto(out *DecidimFileUpload) {
|
||||
*out = *in
|
||||
if in.Available != nil {
|
||||
in, out := &in.Available, &out.Available
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DecidimFileUpload.
|
||||
func (in *DecidimFileUpload) DeepCopy() *DecidimFileUpload {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DecidimFileUpload)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DecidimList) DeepCopyInto(out *DecidimList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Decidim, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DecidimList.
|
||||
func (in *DecidimList) DeepCopy() *DecidimList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DecidimList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DecidimList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DecidimLocale) DeepCopyInto(out *DecidimLocale) {
|
||||
*out = *in
|
||||
if in.Available != nil {
|
||||
in, out := &in.Available, &out.Available
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DecidimLocale.
|
||||
func (in *DecidimLocale) DeepCopy() *DecidimLocale {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DecidimLocale)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DecidimOrganization) DeepCopyInto(out *DecidimOrganization) {
|
||||
*out = *in
|
||||
out.Admin = in.Admin
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DecidimOrganization.
|
||||
func (in *DecidimOrganization) DeepCopy() *DecidimOrganization {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DecidimOrganization)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DecidimOrganizationAdmin) DeepCopyInto(out *DecidimOrganizationAdmin) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DecidimOrganizationAdmin.
|
||||
func (in *DecidimOrganizationAdmin) DeepCopy() *DecidimOrganizationAdmin {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DecidimOrganizationAdmin)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DecidimSpec) DeepCopyInto(out *DecidimSpec) {
|
||||
*out = *in
|
||||
out.Spec = in.Spec
|
||||
out.Admin = in.Admin
|
||||
out.Organization = in.Organization
|
||||
if in.AdditionalHosts != nil {
|
||||
in, out := &in.AdditionalHosts, &out.AdditionalHosts
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AvailableAuthorizations != nil {
|
||||
in, out := &in.AvailableAuthorizations, &out.AvailableAuthorizations
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
in.FileUploadSettings.DeepCopyInto(&out.FileUploadSettings)
|
||||
in.Locale.DeepCopyInto(&out.Locale)
|
||||
if in.EnvFrom != nil {
|
||||
in, out := &in.EnvFrom, &out.EnvFrom
|
||||
*out = make([]v1.EnvFromSource, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DecidimSpec.
|
||||
func (in *DecidimSpec) DeepCopy() *DecidimSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DecidimSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DecidimStatus) DeepCopyInto(out *DecidimStatus) {
|
||||
*out = *in
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DecidimStatus.
|
||||
func (in *DecidimStatus) DeepCopy() *DecidimStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DecidimStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Forgejo) DeepCopyInto(out *Forgejo) {
|
||||
*out = *in
|
||||
|
|
17
cmd/main.go
17
cmd/main.go
|
@ -23,10 +23,12 @@ import (
|
|||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||
// to ensure that exec-entrypoint and run can make use of them.
|
||||
|
||||
"k8s.io/client-go/discovery"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
|
||||
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
|
||||
imcv1alpha1 "github.com/stakater/IngressMonitorController/v2/api/v1alpha1"
|
||||
zalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
|
@ -62,6 +64,7 @@ func init() {
|
|||
utilruntime.Must(zalandov1.AddToScheme(scheme))
|
||||
utilruntime.Must(monitoringv1.AddToScheme(scheme))
|
||||
utilruntime.Must(certmanagerv1.AddToScheme(scheme))
|
||||
utilruntime.Must(imcv1alpha1.AddToScheme(scheme))
|
||||
|
||||
utilruntime.Must(corev1alpha1.AddToScheme(scheme))
|
||||
utilruntime.Must(postgresv1alpha1.AddToScheme(scheme))
|
||||
|
@ -127,6 +130,13 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
discoveryClient := discovery.NewDiscoveryClientForConfigOrDie(mgr.GetConfig())
|
||||
endpointMonitorEnabled, err := discovery.IsResourceEnabled(discoveryClient, imcv1alpha1.GroupVersion.WithResource("endpointmonitors"))
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to check if resource is enabled")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&corecontroller.PostgresReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
|
@ -214,6 +224,13 @@ func main() {
|
|||
setupLog.Error(err, "unable to create controller", "controller", "Forgejo")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err = (&appscontroller.DecidimReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
EndpointMonitorEnabled: endpointMonitorEnabled,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Decidim")
|
||||
os.Exit(1)
|
||||
}
|
||||
//+kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
|
|
239
config/crd/bases/apps.libre.sh_decidims.yaml
Normal file
239
config/crd/bases/apps.libre.sh_decidims.yaml
Normal file
|
@ -0,0 +1,239 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.13.0
|
||||
name: decidims.apps.libre.sh
|
||||
spec:
|
||||
group: apps.libre.sh
|
||||
names:
|
||||
kind: Decidim
|
||||
listKind: DecidimList
|
||||
plural: decidims
|
||||
singular: decidim
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||
name: Ready
|
||||
type: string
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].message
|
||||
name: Status
|
||||
type: string
|
||||
- jsonPath: .status.version
|
||||
name: Version
|
||||
type: string
|
||||
- jsonPath: .spec.suspend
|
||||
name: Suspended
|
||||
type: boolean
|
||||
name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Decidim is the Schema for the decidims API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: DecidimSpec defines the desired state of Decidim
|
||||
properties:
|
||||
additionalHosts:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
admin:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
required:
|
||||
- email
|
||||
type: object
|
||||
availableAuthorizations:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
envFrom:
|
||||
description: OmniAuth DecidimOmniAuth `json:"omniauth,omitempty"`
|
||||
items:
|
||||
description: EnvFromSource represents the source of a set of ConfigMaps
|
||||
properties:
|
||||
configMapRef:
|
||||
description: The ConfigMap to select from
|
||||
properties:
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the ConfigMap must be defined
|
||||
type: boolean
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
prefix:
|
||||
description: An optional identifier to prepend to each key in
|
||||
the ConfigMap. Must be a C_IDENTIFIER.
|
||||
type: string
|
||||
secretRef:
|
||||
description: The Secret to select from
|
||||
properties:
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the Secret must be defined
|
||||
type: boolean
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
type: object
|
||||
type: array
|
||||
fileUploadSettings:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
forceUsersToAuthenticateBeforeAccessOrganization:
|
||||
type: boolean
|
||||
host:
|
||||
minLength: 3
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
locale:
|
||||
properties:
|
||||
available:
|
||||
default:
|
||||
- fr
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
default:
|
||||
default: fr
|
||||
type: string
|
||||
type: object
|
||||
organization:
|
||||
properties:
|
||||
admin:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
nickname:
|
||||
type: string
|
||||
required:
|
||||
- email
|
||||
- name
|
||||
- nickname
|
||||
type: object
|
||||
id:
|
||||
default: 1
|
||||
type: integer
|
||||
type: object
|
||||
suspend:
|
||||
type: boolean
|
||||
timeZone:
|
||||
default: UTC
|
||||
type: string
|
||||
usersRegistrationMode:
|
||||
type: integer
|
||||
required:
|
||||
- host
|
||||
- image
|
||||
- locale
|
||||
- organization
|
||||
type: object
|
||||
status:
|
||||
description: DecidimStatus defines the observed state of Decidim
|
||||
properties:
|
||||
conditions:
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource. --- This struct is intended for direct
|
||||
use as an array at the field path .status.conditions. For example,
|
||||
\n type FooStatus struct{ // Represents the observations of a
|
||||
foo's current state. // Known .status.conditions.type are: \"Available\",
|
||||
\"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
|
||||
// +listType=map // +listMapKey=type Conditions []metav1.Condition
|
||||
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
|
||||
protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: lastTransitionTime is the last time the condition
|
||||
transitioned from one status to another. This should be when
|
||||
the underlying condition changed. If that is not known, then
|
||||
using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: message is a human readable message indicating
|
||||
details about the transition. This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: observedGeneration represents the .metadata.generation
|
||||
that the condition was set based upon. For instance, if .metadata.generation
|
||||
is currently 12, but the .status.conditions[x].observedGeneration
|
||||
is 9, the condition is out of date with respect to the current
|
||||
state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: reason contains a programmatic identifier indicating
|
||||
the reason for the condition's last transition. Producers
|
||||
of specific condition types may define expected values and
|
||||
meanings for this field, and whether the values are considered
|
||||
a guaranteed API. The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
--- Many .condition.type values are consistent across resources
|
||||
like Available, but because arbitrary conditions can be useful
|
||||
(see .node.status.conditions), the ability to deconflict is
|
||||
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- type
|
||||
x-kubernetes-list-type: map
|
||||
version:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
|
@ -14,6 +14,7 @@ resources:
|
|||
- bases/apps.libre.sh_mobilizons.yaml
|
||||
- bases/apps.libre.sh_hedgedocs.yaml
|
||||
- bases/apps.libre.sh_forgejoes.yaml
|
||||
- bases/apps.libre.sh_decidims.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizeresource
|
||||
|
||||
patches:
|
||||
|
@ -31,6 +32,7 @@ patches:
|
|||
#- path: patches/webhook_in_apps_mobilizons.yaml
|
||||
#- path: patches/webhook_in_apps_hedgedocs.yaml
|
||||
#- path: patches/webhook_in_apps_forgejoes.yaml
|
||||
#- path: patches/webhook_in_apps_decidims.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizewebhookpatch
|
||||
|
||||
# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
|
||||
|
@ -47,6 +49,7 @@ patches:
|
|||
#- path: patches/cainjection_in_apps_mobilizons.yaml
|
||||
#- path: patches/cainjection_in_apps_hedgedocs.yaml
|
||||
#- path: patches/cainjection_in_apps_forgejoes.yaml
|
||||
#- path: patches/cainjection_in_apps_decidims.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizecainjectionpatch
|
||||
|
||||
# [WEBHOOK] To enable webhook, uncomment the following section
|
||||
|
|
31
config/rbac/apps_decidim_editor_role.yaml
Normal file
31
config/rbac/apps_decidim_editor_role.yaml
Normal file
|
@ -0,0 +1,31 @@
|
|||
# permissions for end users to edit decidims.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: clusterrole
|
||||
app.kubernetes.io/instance: decidim-editor-role
|
||||
app.kubernetes.io/component: rbac
|
||||
app.kubernetes.io/created-by: libre-sh
|
||||
app.kubernetes.io/part-of: libre-sh
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: decidim-editor-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- apps.libre.sh
|
||||
resources:
|
||||
- decidims
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- apps.libre.sh
|
||||
resources:
|
||||
- decidims/status
|
||||
verbs:
|
||||
- get
|
27
config/rbac/apps_decidim_viewer_role.yaml
Normal file
27
config/rbac/apps_decidim_viewer_role.yaml
Normal file
|
@ -0,0 +1,27 @@
|
|||
# permissions for end users to view decidims.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: clusterrole
|
||||
app.kubernetes.io/instance: decidim-viewer-role
|
||||
app.kubernetes.io/component: rbac
|
||||
app.kubernetes.io/created-by: libre-sh
|
||||
app.kubernetes.io/part-of: libre-sh
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: decidim-viewer-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- apps.libre.sh
|
||||
resources:
|
||||
- decidims
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- apps.libre.sh
|
||||
resources:
|
||||
- decidims/status
|
||||
verbs:
|
||||
- get
|
|
@ -145,6 +145,32 @@ rules:
|
|||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- apps.libre.sh
|
||||
resources:
|
||||
- decidims
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- apps.libre.sh
|
||||
resources:
|
||||
- decidims/finalizers
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- apps.libre.sh
|
||||
resources:
|
||||
- decidims/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- apps.libre.sh
|
||||
resources:
|
||||
|
@ -197,6 +223,18 @@ rules:
|
|||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- autoscaling
|
||||
resources:
|
||||
- horizontalpodautoscalers
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
|
@ -415,6 +453,18 @@ rules:
|
|||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- endpointmonitor.stakater.com
|
||||
resources:
|
||||
- endpointmonitors
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- keycloak.libre.sh
|
||||
resources:
|
||||
|
|
12
config/samples/apps_v1alpha1_decidim.yaml
Normal file
12
config/samples/apps_v1alpha1_decidim.yaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
apiVersion: apps.libre.sh/v1alpha1
|
||||
kind: Decidim
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: decidim
|
||||
app.kubernetes.io/instance: decidim-sample
|
||||
app.kubernetes.io/part-of: libre-sh
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
app.kubernetes.io/created-by: libre-sh
|
||||
name: decidim-sample
|
||||
spec:
|
||||
# TODO(user): Add fields here
|
|
@ -12,4 +12,5 @@ resources:
|
|||
- apps_v1alpha1_mobilizon.yaml
|
||||
- apps_v1alpha1_hedgedoc.yaml
|
||||
- apps_v1alpha1_forgejo.yaml
|
||||
- apps_v1alpha1_decidim.yaml
|
||||
#+kubebuilder:scaffold:manifestskustomizesamples
|
||||
|
|
5
go.mod
5
go.mod
|
@ -18,7 +18,7 @@ require (
|
|||
github.com/fluxcd/pkg/runtime v0.42.0
|
||||
github.com/fluxcd/pkg/ssa v0.35.0
|
||||
github.com/fluxcd/source-controller/api v1.2.3
|
||||
github.com/go-logr/logr v1.3.0
|
||||
github.com/go-logr/logr v1.4.1
|
||||
github.com/google/uuid v1.5.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/minio/madmin-go/v3 v3.0.7
|
||||
|
@ -27,6 +27,7 @@ require (
|
|||
github.com/onsi/gomega v1.30.0
|
||||
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.70.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stakater/IngressMonitorController/v2 v2.1.50
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/tidwall/gjson v1.17.0
|
||||
github.com/tidwall/sjson v1.2.5
|
||||
|
@ -141,7 +142,7 @@ require (
|
|||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.25.0 // indirect
|
||||
golang.org/x/crypto v0.16.0 // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/oauth2 v0.14.0 // indirect
|
||||
|
|
9
go.sum
9
go.sum
|
@ -106,8 +106,9 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS
|
|||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
|
||||
github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
|
@ -325,6 +326,8 @@ github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
|||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stakater/IngressMonitorController/v2 v2.1.50 h1:7NN/7edU+8VK7+V7NNVgjv5Y35zhRecQ8aTEcEvWGFg=
|
||||
github.com/stakater/IngressMonitorController/v2 v2.1.50/go.mod h1:oDNe9M+84mS2IvicLf39dloF9BIt2sAf1JcI0drwpw4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
|
@ -384,8 +387,8 @@ golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
|
|
264
internal/controller/apps/decidim_controller.go
Normal file
264
internal/controller/apps/decidim_controller.go
Normal file
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/runtime/patch"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshcore "libre.sh/api/core/v1alpha1"
|
||||
lshmeta "libre.sh/api/meta/v1alpha1"
|
||||
internal "libre.sh/internal/decidim"
|
||||
)
|
||||
|
||||
// DecidimReconciler reconciles a Decidim object
|
||||
type DecidimReconciler struct {
|
||||
client.Client
|
||||
EndpointMonitorEnabled bool
|
||||
}
|
||||
|
||||
type decidimResources struct {
|
||||
postgres *lshcore.Postgres
|
||||
redis *lshcore.Redis
|
||||
bucket *lshcore.Bucket
|
||||
secret *corev1.Secret
|
||||
}
|
||||
|
||||
//+kubebuilder:rbac:groups=apps.libre.sh,resources=decidims,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=apps.libre.sh,resources=decidims/status,verbs=get;update;patch
|
||||
//+kubebuilder:rbac:groups=apps.libre.sh,resources=decidims/finalizers,verbs=update
|
||||
|
||||
//+kubebuilder:rbac:groups=core.libre.sh,resources=postgres;buckets;redis,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=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=autoscaling,resources=horizontalpodautoscalers,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups="",resources=secrets;configmaps;services,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=endpointmonitor.stakater.com,resources=endpointmonitors,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.
|
||||
// TODO(user): Modify the Reconcile function to compare the state specified by
|
||||
// the Decidim object against the actual cluster state, and then
|
||||
// perform operations to make the cluster state reflect the state specified by
|
||||
// the user.
|
||||
//
|
||||
// For more details, check Reconcile and its Result here:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.0/pkg/reconcile
|
||||
func (r *DecidimReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
log := log.FromContext(ctx)
|
||||
|
||||
var decidim appsv1alpha1.Decidim
|
||||
patcher, result := lshr.Initialize(ctx, r, req, &decidim)
|
||||
if result != nil {
|
||||
return result.Unwrap()
|
||||
}
|
||||
|
||||
if lshr.IsFinalizing(&decidim) {
|
||||
return lshr.Finalize(ctx, r, patcher, &decidim, func() error {
|
||||
var bucket lshcore.Bucket
|
||||
lshr.SetResourceNamespacedName(&decidim, &bucket)
|
||||
err := r.Get(ctx, client.ObjectKeyFromObject(&bucket), &bucket)
|
||||
if err != nil {
|
||||
return client.IgnoreNotFound(err)
|
||||
}
|
||||
return lshr.UnsetControllerReference(ctx, r, &decidim, &bucket)
|
||||
})
|
||||
}
|
||||
|
||||
jm := lshr.NewJobManager(r, &decidim, patcher)
|
||||
resources := decidimResources{}
|
||||
|
||||
err := r.reconcilePostgres(ctx, &decidim, &resources)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
err = r.reconcileRedis(ctx, &decidim, &resources)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
err = r.reconcileBucket(ctx, &decidim, &resources)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
lshr.SetDependencyCondition(&decidim, resources.postgres, resources.redis, resources.bucket)
|
||||
if err := lshr.Patch(ctx, r, patcher, &decidim, lshr.PatchOpts{}); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
if lshr.IsDependencyNotReady(&decidim) || lshr.IsImporting(&decidim) {
|
||||
log.Info("Waiting for dependencies or importation")
|
||||
return ctrl.Result{}, nil
|
||||
|
||||
}
|
||||
|
||||
err = r.reconcileSecret(ctx, &decidim, &resources)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
err = lshr.OnInstall(&decidim, func() error {
|
||||
log.Info("Installing")
|
||||
job, err := r.reconcileInstallJob(ctx, &decidim, &resources)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.completeHook(ctx, patcher, &decidim, jm, job)
|
||||
})
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
err = lshr.OnUpgrade(&decidim, func() error {
|
||||
log.Info("Upgrading")
|
||||
job, err := r.reconcileUpgradeJob(ctx, &decidim, &resources)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.completeHook(ctx, patcher, &decidim, jm, job)
|
||||
})
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
if lshr.IsJobNotComleted(&decidim) {
|
||||
log.Info("Waiting for lifecycle job")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
err = r.reconcileMemcachedDeployment(ctx, &decidim)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
err = r.reconcileMemcachedService(ctx, &decidim)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
err = r.reconcileMaintenance(ctx, &decidim, patcher, &resources)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
sidekiqDep, err := r.reconcileSidekiqDeployment(ctx, &decidim, &resources)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
_, err = r.reconcileHpaSidekiq(ctx, &decidim, sidekiqDep)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
dep, err := r.reconcileDeployment(ctx, &decidim, &resources)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
err = r.reconcilePodDisruptionBudget(ctx, &decidim)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
_, err = r.reconcileHpa(ctx, &decidim, dep)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
svc, err := r.reconcileService(ctx, &decidim)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
_, err = r.reconcileIngress(ctx, &decidim, svc)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
if r.EndpointMonitorEnabled {
|
||||
err = r.reconcileIMC(ctx, &decidim)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if !internal.HealthCheck(ctx, svc) {
|
||||
return ctrl.Result{RequeueAfter: 2 * time.Second}, nil
|
||||
}
|
||||
|
||||
return ctrl.Result{}, lshr.Complete(ctx, r, patcher, &decidim, lshr.PatchOpts{})
|
||||
}
|
||||
|
||||
// todo : move to runtime
|
||||
func (r *DecidimReconciler) completeHook(ctx context.Context, patcher *patch.SerialPatcher, decidim *appsv1alpha1.Decidim, jm lshr.JobManager, job *batchv1.Job) error {
|
||||
jm.Add(job)
|
||||
isJobNotComleted, err := jm.UpdateCondition(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isJobNotComleted {
|
||||
lshr.SetCurrentVersion(decidim)
|
||||
delete(decidim.Annotations, "core.libre.sh/lifecycle")
|
||||
err := r.Delete(ctx, job, client.PropagationPolicy(metav1.DeletePropagationBackground))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return lshr.Patch(ctx, r, patcher, decidim, lshr.PatchOpts{})
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *DecidimReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&appsv1alpha1.Decidim{}).
|
||||
Owns(&lshcore.Postgres{}).
|
||||
Owns(&lshcore.Bucket{}).
|
||||
Owns(&lshcore.Redis{}).
|
||||
Owns(&batchv1.Job{}).
|
||||
Owns(&appsv1.Deployment{}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *DecidimReconciler) Name() string {
|
||||
return "decidim-controller"
|
||||
}
|
||||
|
||||
func (r *DecidimReconciler) OwnedConditions() []string {
|
||||
return []string{
|
||||
meta.ReconcilingCondition,
|
||||
meta.StalledCondition,
|
||||
lshmeta.DependenciesNotReady,
|
||||
lshmeta.HookJobNotComleted,
|
||||
}
|
||||
}
|
65
internal/controller/apps/decidim_controller_affinity.go
Normal file
65
internal/controller/apps/decidim_controller_affinity.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) setAffinity(podSpec *corev1.PodSpec) {
|
||||
podSpec.Affinity = &corev1.Affinity{
|
||||
NodeAffinity: &corev1.NodeAffinity{
|
||||
PreferredDuringSchedulingIgnoredDuringExecution: []corev1.PreferredSchedulingTerm{
|
||||
{
|
||||
Weight: 20,
|
||||
Preference: corev1.NodeSelectorTerm{
|
||||
MatchExpressions: []corev1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "node.libre.sh/roles",
|
||||
Operator: corev1.NodeSelectorOpDoesNotExist,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Weight: 100,
|
||||
Preference: corev1.NodeSelectorTerm{
|
||||
MatchExpressions: []corev1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "node.libre.sh/roles",
|
||||
Operator: corev1.NodeSelectorOpNotIn,
|
||||
Values: []string{"postgres"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Weight: 80,
|
||||
Preference: corev1.NodeSelectorTerm{
|
||||
MatchExpressions: []corev1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "node.libre.sh/roles",
|
||||
Operator: corev1.NodeSelectorOpNotIn,
|
||||
Values: []string{"minio"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
40
internal/controller/apps/decidim_controller_bucket.go
Normal file
40
internal/controller/apps/decidim_controller_bucket.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshcore "libre.sh/api/core/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) reconcileBucket(ctx context.Context, decidim *appsv1alpha1.Decidim, resources *decidimResources) error {
|
||||
var bucket lshcore.Bucket
|
||||
resources.bucket = &bucket
|
||||
lshr.SetResourceNamespacedName(decidim, &bucket)
|
||||
return lshr.CreateOrPatch(ctx, r, &bucket, func() error {
|
||||
lshr.ApplyLabels(decidim, &bucket, nil)
|
||||
bucket.Spec.Suspend = decidim.GetSuspend()
|
||||
bucket.Spec.Policy = lshcore.BucketPolicy{Preset: lshcore.BucketPrivatePreset}
|
||||
bucket.Spec.Provider = lshcore.BucketDataProvider
|
||||
return controllerutil.SetControllerReference(decidim, &bucket, r.Scheme())
|
||||
})
|
||||
}
|
44
internal/controller/apps/decidim_controller_config.go
Normal file
44
internal/controller/apps/decidim_controller_config.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) reconcileSecret(ctx context.Context, decidim *appsv1alpha1.Decidim, resources *decidimResources) error {
|
||||
var secret corev1.Secret
|
||||
resources.secret = &secret
|
||||
secret.Name = lshr.GetResourceName(decidim)
|
||||
secret.Namespace = decidim.Namespace
|
||||
return lshr.CreateOrPatch(ctx, r, &secret, func() error {
|
||||
if secret.Data == nil {
|
||||
secret.Data = make(map[string][]byte)
|
||||
}
|
||||
if len(secret.Data["SECRET_KEY_BASE"]) == 0 {
|
||||
secret.Data["SECRET_KEY_BASE"] = []byte(rand.String(32))
|
||||
}
|
||||
return controllerutil.SetControllerReference(decidim, &secret, r.Scheme())
|
||||
})
|
||||
}
|
125
internal/controller/apps/decidim_controller_deploy.go
Normal file
125
internal/controller/apps/decidim_controller_deploy.go
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) reconcileDeployment(ctx context.Context, decidim *appsv1alpha1.Decidim, resources *decidimResources) (*appsv1.Deployment, error) {
|
||||
var deployment appsv1.Deployment
|
||||
lshr.SetResourceNamespacedName(decidim, &deployment, "app")
|
||||
err := lshr.CreateOrPatch(ctx, r, &deployment, func() error {
|
||||
lshr.ApplyLabels(decidim, &deployment, &lshr.LabelOpts{
|
||||
Component: "app",
|
||||
Version: decidim.GetVersion(),
|
||||
})
|
||||
deployment.Spec.Selector = &metav1.LabelSelector{
|
||||
MatchLabels: lshr.ExtractLabelSelector(&deployment),
|
||||
}
|
||||
deployment.Spec.Template.ObjectMeta.Labels = deployment.Labels
|
||||
deployment.Spec.Template.Spec.TopologySpreadConstraints = []corev1.TopologySpreadConstraint{
|
||||
{
|
||||
MaxSkew: 1,
|
||||
TopologyKey: "kubernetes.io/hostname",
|
||||
WhenUnsatisfiable: corev1.ScheduleAnyway,
|
||||
LabelSelector: deployment.Spec.Selector,
|
||||
},
|
||||
}
|
||||
|
||||
if deployment.Spec.Replicas == nil {
|
||||
replicas := int32(2)
|
||||
deployment.Spec.Replicas = &replicas
|
||||
}
|
||||
|
||||
r.setAffinity(&deployment.Spec.Template.Spec)
|
||||
|
||||
probeHandler := corev1.ProbeHandler{
|
||||
HTTPGet: &corev1.HTTPGetAction{
|
||||
Path: "/health_check",
|
||||
Port: intstr.FromString("http"),
|
||||
},
|
||||
}
|
||||
appContainer := corev1.Container{
|
||||
Image: decidim.Spec.Image,
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
Name: "app",
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
ContainerPort: 3000,
|
||||
Name: "http",
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
// TODO no endpoint pour readiness and liveness ?
|
||||
// TODO no securityContext ?
|
||||
EnvFrom: append(decidim.Spec.EnvFrom, corev1.EnvFromSource{
|
||||
SecretRef: &corev1.SecretEnvSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.secret.Name,
|
||||
},
|
||||
},
|
||||
}),
|
||||
StartupProbe: &corev1.Probe{
|
||||
ProbeHandler: probeHandler,
|
||||
InitialDelaySeconds: 10,
|
||||
FailureThreshold: 11,
|
||||
PeriodSeconds: 10,
|
||||
},
|
||||
ReadinessProbe: &corev1.Probe{
|
||||
ProbeHandler: probeHandler,
|
||||
PeriodSeconds: 10,
|
||||
TimeoutSeconds: 30,
|
||||
FailureThreshold: 3,
|
||||
},
|
||||
LivenessProbe: &corev1.Probe{
|
||||
ProbeHandler: probeHandler,
|
||||
PeriodSeconds: 60,
|
||||
TimeoutSeconds: 30,
|
||||
FailureThreshold: 3,
|
||||
},
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("100m"),
|
||||
corev1.ResourceMemory: resource.MustParse("500Mi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceMemory: resource.MustParse("2Gi"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
envVars := r.getEnvVars(decidim, resources)
|
||||
|
||||
appContainer.Env = envVars
|
||||
|
||||
deployment.Spec.Template.Spec.Containers = []corev1.Container{appContainer /* , webContainer */}
|
||||
return controllerutil.SetControllerReference(decidim, &deployment, r.Scheme())
|
||||
})
|
||||
|
||||
return &deployment, err
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) reconcileSidekiqDeployment(ctx context.Context, decidim *appsv1alpha1.Decidim, resources *decidimResources) (*appsv1.Deployment, error) {
|
||||
const component = "sidekiq"
|
||||
var deployment appsv1.Deployment
|
||||
lshr.SetResourceNamespacedName(decidim, &deployment, component)
|
||||
err := lshr.CreateOrPatch(ctx, r, &deployment, func() error {
|
||||
lshr.ApplyLabels(decidim, &deployment, &lshr.LabelOpts{
|
||||
Component: component,
|
||||
Version: decidim.GetVersion(),
|
||||
})
|
||||
deployment.Spec.Selector = &metav1.LabelSelector{
|
||||
MatchLabels: lshr.ExtractLabelSelector(&deployment),
|
||||
}
|
||||
deployment.Spec.Template.ObjectMeta.Labels = deployment.Labels
|
||||
deployment.Spec.Template.ObjectMeta = metav1.ObjectMeta{
|
||||
Labels: deployment.Labels,
|
||||
}
|
||||
|
||||
r.setAffinity(&deployment.Spec.Template.Spec)
|
||||
|
||||
container := corev1.Container{
|
||||
Image: decidim.Spec.Image,
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
Name: "sidekiq",
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
ContainerPort: 7433,
|
||||
Name: "api",
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
Command: []string{"sidekiq", "-C", "config/sidekiq.yml"},
|
||||
Lifecycle: &corev1.Lifecycle{
|
||||
PreStop: &corev1.LifecycleHandler{
|
||||
Exec: &corev1.ExecAction{
|
||||
Command: []string{"./sidekiq_quiet.sh"},
|
||||
},
|
||||
},
|
||||
},
|
||||
// TODO no securityContext ?
|
||||
EnvFrom: append(decidim.Spec.EnvFrom, corev1.EnvFromSource{
|
||||
SecretRef: &corev1.SecretEnvSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.secret.Name,
|
||||
},
|
||||
},
|
||||
}),
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("100m"),
|
||||
corev1.ResourceMemory: resource.MustParse("500Mi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("500m"),
|
||||
corev1.ResourceMemory: resource.MustParse("2Gi"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
envVars := r.getEnvVars(decidim, resources)
|
||||
|
||||
container.Env = envVars
|
||||
|
||||
deployment.Spec.Template.Spec.Containers = []corev1.Container{container}
|
||||
|
||||
return controllerutil.SetControllerReference(decidim, &deployment, r.Scheme())
|
||||
})
|
||||
return &deployment, err
|
||||
}
|
183
internal/controller/apps/decidim_controller_env.go
Normal file
183
internal/controller/apps/decidim_controller_env.go
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshmeta "libre.sh/api/meta/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) getEnvVars(decidim *appsv1alpha1.Decidim, resources *decidimResources) []corev1.EnvVar {
|
||||
return []corev1.EnvVar{
|
||||
/*
|
||||
|
||||
FRANCE_CONNECT_PROFILE_IDENTIFIER
|
||||
FRANCE_CONNECT_PROFILE_SECRET
|
||||
FRANCE_CONNECT_UID_IDENTIFIER
|
||||
FRANCE_CONNECT_UID_SECRET
|
||||
RAILS_ENV
|
||||
SECRET_KEY_BASE
|
||||
SESSION_DAYS_TRIM_THRESHOLD
|
||||
*/
|
||||
{Name: "RAILS_ENV", Value: "production"},
|
||||
{Name: "APP_NAME", Value: decidim.Name},
|
||||
{Name: "WEB_CONCURRENCY", Value: "0"},
|
||||
{Name: "CACHE_ASSETS", Value: "true"},
|
||||
{Name: "RAILS_LOG_TO_STDOUT", Value: "true"},
|
||||
{Name: "RAILS_SERVE_STATIC_FILES", Value: "true"},
|
||||
{Name: "NEW_RELIC_ENABLED", Value: "false"},
|
||||
{Name: "FORCE_SSL", Value: "1"},
|
||||
{Name: "DEFAULT_LOCALE", Value: decidim.Spec.Locale.Default},
|
||||
{Name: "AVAILABLE_LOCALES", Value: strings.Join(decidim.Spec.Locale.Available, ",")},
|
||||
|
||||
{Name: "ENABLE_RACK_ATTACK", Value: "0"},
|
||||
{Name: "RACK_ATTACK_FAIL2BAN", Value: "0"},
|
||||
|
||||
{Name: "ASSET_HOST", Value: decidim.Spec.Host},
|
||||
{
|
||||
Name: "MEMCACHE_SERVERS",
|
||||
Value: fmt.Sprintf("%s:%d", lshr.GetResourceName(decidim, "memcached"), MEMCACHED_PORT),
|
||||
},
|
||||
{
|
||||
Name: "POD_IP",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
FieldRef: &corev1.ObjectFieldSelector{
|
||||
FieldPath: "status.podIP",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "SCALEWAY_ID",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.bucket.SecretName(),
|
||||
},
|
||||
Key: "accessKey",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "SCALEWAY_TOKEN",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.bucket.SecretName(),
|
||||
},
|
||||
Key: "secretKey",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "OBJECTSTORE_S3_HOST",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.bucket.SecretName(),
|
||||
},
|
||||
Key: "endpoint",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "SCALEWAY_BUCKET_NAME",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.bucket.SecretName(),
|
||||
},
|
||||
Key: "bucket",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "DATABASE_USERNAME",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.postgres.SecretName(),
|
||||
},
|
||||
Key: lshmeta.SecretUsernameKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "DATABASE_PASSWORD",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.postgres.SecretName(),
|
||||
},
|
||||
Key: lshmeta.SecretPasswordKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "DATABASE_HOST",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.postgres.SecretName(),
|
||||
},
|
||||
Key: lshmeta.SecretHostKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "DATABASE_PORT",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.postgres.SecretName(),
|
||||
},
|
||||
Key: lshmeta.SecretPortKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "DATABASE_NAME",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.postgres.SecretName(),
|
||||
},
|
||||
Key: "database",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "REDIS_URL",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.redis.SecretName(),
|
||||
},
|
||||
Key: "url",
|
||||
},
|
||||
},
|
||||
},
|
||||
// TODO no port for redis need to be specified ?
|
||||
// TODO add smtp
|
||||
// TODO check all secrets/configmap that should be set
|
||||
}
|
||||
}
|
95
internal/controller/apps/decidim_controller_hpa.go
Normal file
95
internal/controller/apps/decidim_controller_hpa.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
autoscalingv2 "k8s.io/api/autoscaling/v2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) reconcileHpa(ctx context.Context, decidim *appsv1alpha1.Decidim, deployment *appsv1.Deployment) (*autoscalingv2.HorizontalPodAutoscaler, error) {
|
||||
var hpa autoscalingv2.HorizontalPodAutoscaler
|
||||
lshr.SetResourceNamespacedName(decidim, &hpa, "app")
|
||||
err := lshr.CreateOrPatch(ctx, r, &hpa, func() error {
|
||||
lshr.ApplyLabels(decidim, &hpa, &lshr.LabelOpts{
|
||||
Component: "app",
|
||||
Version: decidim.GetVersion(),
|
||||
})
|
||||
hpa.Spec.ScaleTargetRef = autoscalingv2.CrossVersionObjectReference{
|
||||
APIVersion: appsv1.SchemeGroupVersion.String(),
|
||||
Kind: "Deployment",
|
||||
Name: deployment.Name,
|
||||
}
|
||||
min := int32(2)
|
||||
hpa.Spec.MinReplicas = &min
|
||||
hpa.Spec.MaxReplicas = 40
|
||||
var averageUtilization int32 = 150
|
||||
hpa.Spec.Metrics = []autoscalingv2.MetricSpec{
|
||||
{
|
||||
Type: autoscalingv2.ResourceMetricSourceType,
|
||||
Resource: &autoscalingv2.ResourceMetricSource{
|
||||
Name: "cpu",
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.UtilizationMetricType,
|
||||
AverageUtilization: &averageUtilization,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var stabilizationWindowSeconds int32 = 300
|
||||
hpa.Spec.Behavior = &autoscalingv2.HorizontalPodAutoscalerBehavior{
|
||||
ScaleUp: &autoscalingv2.HPAScalingRules{
|
||||
StabilizationWindowSeconds: &stabilizationWindowSeconds,
|
||||
Policies: []autoscalingv2.HPAScalingPolicy{
|
||||
{
|
||||
Type: autoscalingv2.PodsScalingPolicy,
|
||||
Value: 1,
|
||||
PeriodSeconds: 15,
|
||||
},
|
||||
{
|
||||
Type: autoscalingv2.PercentScalingPolicy,
|
||||
Value: 50,
|
||||
PeriodSeconds: 15,
|
||||
},
|
||||
},
|
||||
},
|
||||
ScaleDown: &autoscalingv2.HPAScalingRules{
|
||||
StabilizationWindowSeconds: &stabilizationWindowSeconds,
|
||||
Policies: []autoscalingv2.HPAScalingPolicy{
|
||||
{
|
||||
Type: autoscalingv2.PodsScalingPolicy,
|
||||
Value: 1,
|
||||
PeriodSeconds: 15,
|
||||
},
|
||||
{
|
||||
Type: autoscalingv2.PodsScalingPolicy,
|
||||
Value: 25,
|
||||
PeriodSeconds: 15,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return controllerutil.SetControllerReference(decidim, &hpa, r.Scheme())
|
||||
})
|
||||
return &hpa, err
|
||||
}
|
85
internal/controller/apps/decidim_controller_hpa_sidekiq.go
Normal file
85
internal/controller/apps/decidim_controller_hpa_sidekiq.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
autoscalingv2 "k8s.io/api/autoscaling/v2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) reconcileHpaSidekiq(ctx context.Context, decidim *appsv1alpha1.Decidim, deployment *appsv1.Deployment) (*autoscalingv2.HorizontalPodAutoscaler, error) {
|
||||
var hpa autoscalingv2.HorizontalPodAutoscaler
|
||||
lshr.SetResourceNamespacedName(decidim, &hpa, "sidekiq")
|
||||
err := lshr.CreateOrPatch(ctx, r, &hpa, func() error {
|
||||
lshr.ApplyLabels(decidim, &hpa, &lshr.LabelOpts{
|
||||
Component: "sidekiq",
|
||||
Version: decidim.GetVersion(),
|
||||
})
|
||||
hpa.Spec.ScaleTargetRef = autoscalingv2.CrossVersionObjectReference{
|
||||
APIVersion: appsv1.SchemeGroupVersion.String(),
|
||||
Kind: "Deployment",
|
||||
Name: deployment.Name,
|
||||
}
|
||||
min := int32(1)
|
||||
hpa.Spec.MinReplicas = &min
|
||||
hpa.Spec.MaxReplicas = 5
|
||||
var averageUtilization int32 = 150
|
||||
hpa.Spec.Metrics = []autoscalingv2.MetricSpec{
|
||||
{
|
||||
Type: autoscalingv2.ResourceMetricSourceType,
|
||||
Resource: &autoscalingv2.ResourceMetricSource{
|
||||
Name: "cpu",
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.UtilizationMetricType,
|
||||
AverageUtilization: &averageUtilization,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var stabilizationWindowSeconds int32 = 300
|
||||
hpa.Spec.Behavior = &autoscalingv2.HorizontalPodAutoscalerBehavior{
|
||||
ScaleUp: &autoscalingv2.HPAScalingRules{
|
||||
StabilizationWindowSeconds: &stabilizationWindowSeconds,
|
||||
Policies: []autoscalingv2.HPAScalingPolicy{
|
||||
{
|
||||
Type: autoscalingv2.PodsScalingPolicy,
|
||||
Value: 1,
|
||||
PeriodSeconds: 30,
|
||||
},
|
||||
},
|
||||
},
|
||||
ScaleDown: &autoscalingv2.HPAScalingRules{
|
||||
StabilizationWindowSeconds: &stabilizationWindowSeconds,
|
||||
Policies: []autoscalingv2.HPAScalingPolicy{
|
||||
{
|
||||
Type: autoscalingv2.PodsScalingPolicy,
|
||||
Value: 1,
|
||||
PeriodSeconds: 15,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return controllerutil.SetControllerReference(decidim, &hpa, r.Scheme())
|
||||
})
|
||||
return &hpa, err
|
||||
}
|
104
internal/controller/apps/decidim_controller_ingress.go
Normal file
104
internal/controller/apps/decidim_controller_ingress.go
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
imcv1alpha1 "github.com/stakater/IngressMonitorController/v2/api/v1alpha1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) getIngressRule(decidim *appsv1alpha1.Decidim, svc *corev1.Service, host string) networkingv1.IngressRule {
|
||||
pathType := networkingv1.PathTypePrefix
|
||||
return networkingv1.IngressRule{
|
||||
Host: host,
|
||||
IngressRuleValue: networkingv1.IngressRuleValue{
|
||||
HTTP: &networkingv1.HTTPIngressRuleValue{
|
||||
Paths: []networkingv1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/",
|
||||
PathType: &pathType,
|
||||
Backend: networkingv1.IngressBackend{
|
||||
Service: &networkingv1.IngressServiceBackend{
|
||||
Name: svc.Name,
|
||||
Port: networkingv1.ServiceBackendPort{
|
||||
Name: "http",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *DecidimReconciler) reconcileIngress(ctx context.Context, decidim *appsv1alpha1.Decidim, svc *corev1.Service) (*networkingv1.Ingress, error) {
|
||||
var ingress networkingv1.Ingress
|
||||
lshr.SetResourceNamespacedName(decidim, &ingress)
|
||||
err := lshr.CreateOrPatch(ctx, r, &ingress, func() error {
|
||||
ingress.ObjectMeta.Annotations = map[string]string{
|
||||
"kubernetes.io/tls-acme": "true",
|
||||
"nginx.ingress.kubernetes.io/proxy-body-size": "100g",
|
||||
"nginx.ingress.kubernetes.io/proxy-request-buffering": "off",
|
||||
}
|
||||
|
||||
ingressTLS := networkingv1.IngressTLS{
|
||||
Hosts: []string{decidim.Spec.Host},
|
||||
SecretName: fmt.Sprintf("%s-tls", decidim.Name),
|
||||
}
|
||||
|
||||
ingress.Spec.Rules = []networkingv1.IngressRule{
|
||||
r.getIngressRule(decidim, svc, decidim.Spec.Host),
|
||||
}
|
||||
|
||||
for _, additionalHost := range decidim.Spec.AdditionalHosts {
|
||||
ingressTLS.Hosts = append(ingressTLS.Hosts, additionalHost)
|
||||
ingress.Spec.Rules = append(ingress.Spec.Rules, r.getIngressRule(decidim, svc, additionalHost))
|
||||
}
|
||||
|
||||
ingress.Spec.TLS = []networkingv1.IngressTLS{ingressTLS}
|
||||
|
||||
return controllerutil.SetControllerReference(decidim, &ingress, r.Scheme())
|
||||
})
|
||||
return &ingress, err
|
||||
}
|
||||
|
||||
func (r *DecidimReconciler) reconcileIMC(ctx context.Context, decidim *appsv1alpha1.Decidim) error {
|
||||
var imc imcv1alpha1.EndpointMonitor
|
||||
lshr.SetResourceNamespacedName(decidim, &imc)
|
||||
return lshr.CreateOrPatch(ctx, r, &imc, func() error {
|
||||
imc.Spec.ForceHTTPS = true
|
||||
imc.Spec.HealthEndpoint = "/"
|
||||
imc.Spec.URLFrom = &imcv1alpha1.URLSource{
|
||||
IngressRef: &imcv1alpha1.IngressURLSource{
|
||||
Name: imc.Name,
|
||||
},
|
||||
}
|
||||
imc.Spec.UptimeRobotConfig = &imcv1alpha1.UptimeRobotConfig{
|
||||
Interval: 60,
|
||||
}
|
||||
return controllerutil.SetControllerReference(decidim, &imc, r.Scheme())
|
||||
})
|
||||
}
|
86
internal/controller/apps/decidim_controller_job.go
Normal file
86
internal/controller/apps/decidim_controller_job.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) mutateJob(decidim *appsv1alpha1.Decidim, resources *decidimResources, job *batchv1.Job, command ...string) {
|
||||
r.setAffinity(&job.Spec.Template.Spec)
|
||||
container := corev1.Container{
|
||||
Image: decidim.Spec.Image,
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
Command: command,
|
||||
Name: "decidim",
|
||||
|
||||
Env: r.getEnvVars(decidim, resources),
|
||||
EnvFrom: append(decidim.Spec.EnvFrom, corev1.EnvFromSource{
|
||||
SecretRef: &corev1.SecretEnvSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.secret.Name,
|
||||
},
|
||||
},
|
||||
}),
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("500m"),
|
||||
corev1.ResourceMemory: resource.MustParse("512Mi"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
backoffLimit := int32(0)
|
||||
job.Spec.BackoffLimit = &backoffLimit
|
||||
/* job.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{
|
||||
RunAsUser: &UserID,
|
||||
RunAsGroup: &UserID,
|
||||
FSGroup: &UserID,
|
||||
} */
|
||||
job.Spec.Template.Spec.Containers = []corev1.Container{container}
|
||||
job.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyNever
|
||||
}
|
||||
|
||||
func (r *DecidimReconciler) reconcileInstallJob(ctx context.Context, decidim *appsv1alpha1.Decidim, resources *decidimResources) (*batchv1.Job, error) {
|
||||
var job batchv1.Job
|
||||
job.Name = lshr.GetResourceName(decidim, "install")
|
||||
job.Namespace = decidim.Namespace
|
||||
return &job, lshr.CreateOrPatch(ctx, r, &job, func() error {
|
||||
r.mutateJob(decidim, resources, &job,
|
||||
"bundle", "exec", "rails", "db:create", "db:migrate")
|
||||
return controllerutil.SetControllerReference(decidim, &job, r.Scheme())
|
||||
})
|
||||
}
|
||||
|
||||
func (r *DecidimReconciler) reconcileUpgradeJob(ctx context.Context, decidim *appsv1alpha1.Decidim, resources *decidimResources) (*batchv1.Job, error) {
|
||||
var job batchv1.Job
|
||||
job.Name = lshr.GetResourceName(decidim, "upgrade")
|
||||
job.Namespace = decidim.Namespace
|
||||
return &job, lshr.CreateOrPatch(ctx, r, &job, func() error {
|
||||
r.mutateJob(decidim, resources, &job,
|
||||
"rake", "decidim_app:k8s:upgrade")
|
||||
return controllerutil.SetControllerReference(decidim, &job, r.Scheme())
|
||||
})
|
||||
}
|
106
internal/controller/apps/decidim_controller_maintenance.go
Normal file
106
internal/controller/apps/decidim_controller_maintenance.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/fluxcd/pkg/runtime/patch"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
const maintenanceAnnotation = "decidim.libre.sh/maintenance"
|
||||
|
||||
func (r *DecidimReconciler) reconcileMaintenance(ctx context.Context, decidim *appsv1alpha1.Decidim, patcher *patch.SerialPatcher, resources *decidimResources) error {
|
||||
log := log.FromContext(ctx)
|
||||
var job batchv1.Job
|
||||
lshr.SetResourceNamespacedName(decidim, &job, "maintenance")
|
||||
|
||||
if decidim.Annotations[maintenanceAnnotation] == "" {
|
||||
err := r.Delete(ctx, &job, client.PropagationPolicy(metav1.DeletePropagationForeground))
|
||||
if err == nil {
|
||||
log.Info("Maintenance job deleted")
|
||||
}
|
||||
return client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
minutes, err := strconv.Atoi(decidim.Annotations[maintenanceAnnotation])
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s should be a number of minutes: %w", maintenanceAnnotation, err)
|
||||
}
|
||||
|
||||
err = lshr.CreateOrPatch(ctx, r, &job, func() error {
|
||||
if job.CreationTimestamp.IsZero() {
|
||||
log.Info("Creating maintenance job")
|
||||
}
|
||||
lshr.ApplyLabels(decidim, &job, &lshr.LabelOpts{
|
||||
Component: "maintenance",
|
||||
Version: decidim.GetVersion(),
|
||||
})
|
||||
container := corev1.Container{
|
||||
Image: decidim.Spec.Image,
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
Name: "main",
|
||||
Command: []string{"sh", "-c"},
|
||||
Args: []string{fmt.Sprintf("echo waiting %dm && sleep %dm", minutes, minutes)},
|
||||
Env: r.getEnvVars(decidim, resources),
|
||||
EnvFrom: append(decidim.Spec.EnvFrom, corev1.EnvFromSource{
|
||||
SecretRef: &corev1.SecretEnvSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: resources.secret.Name,
|
||||
},
|
||||
},
|
||||
}),
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("240m"),
|
||||
corev1.ResourceMemory: resource.MustParse("500Mi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceMemory: resource.MustParse("6Gi"),
|
||||
},
|
||||
},
|
||||
}
|
||||
job.Spec.Template.Spec.Containers = []corev1.Container{container}
|
||||
job.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyOnFailure
|
||||
gracePeriod := int64(0)
|
||||
job.Spec.Template.Spec.TerminationGracePeriodSeconds = &gracePeriod
|
||||
return controllerutil.SetControllerReference(decidim, &job, r.Scheme())
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !job.Status.CompletionTime.IsZero() {
|
||||
log.Info("Maintenance job is complete, marking it for deletion")
|
||||
delete(decidim.Annotations, maintenanceAnnotation)
|
||||
return lshr.Patch(ctx, r, patcher, decidim, lshr.PatchOpts{DisableReadyCondition: true})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
98
internal/controller/apps/decidim_controller_memcached.go
Normal file
98
internal/controller/apps/decidim_controller_memcached.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
const MEMCACHED_PORT = 11211
|
||||
|
||||
func (r *DecidimReconciler) reconcileMemcachedDeployment(ctx context.Context, decidim *appsv1alpha1.Decidim) error {
|
||||
var deployment appsv1.Deployment
|
||||
lshr.SetResourceNamespacedName(decidim, &deployment, "memcached")
|
||||
return lshr.CreateOrPatch(ctx, r, &deployment, func() error {
|
||||
lshr.ApplyLabels(decidim, &deployment, &lshr.LabelOpts{
|
||||
Component: "memcached",
|
||||
Version: decidim.GetVersion(),
|
||||
})
|
||||
deployment.Spec.Selector = &metav1.LabelSelector{
|
||||
MatchLabels: lshr.ExtractLabelSelector(&deployment),
|
||||
}
|
||||
deployment.Spec.Template.ObjectMeta.Labels = deployment.Labels
|
||||
replicas := int32(1)
|
||||
deployment.Spec.Replicas = &replicas
|
||||
deployment.Spec.Template.Spec.Containers = []corev1.Container{
|
||||
{
|
||||
Image: "docker.io/memcached:1.6",
|
||||
Name: "memcached",
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
ContainerPort: MEMCACHED_PORT,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("5m"),
|
||||
corev1.ResourceMemory: resource.MustParse("32Mi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("200m"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return controllerutil.SetControllerReference(decidim, &deployment, r.Scheme())
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (r *DecidimReconciler) reconcileMemcachedService(ctx context.Context, decidim *appsv1alpha1.Decidim) error {
|
||||
var svc corev1.Service
|
||||
lshr.SetResourceNamespacedName(decidim, &svc, "memcached")
|
||||
return lshr.CreateOrPatch(ctx, r, &svc, func() error {
|
||||
lshr.ApplyLabels(decidim, &svc, &lshr.LabelOpts{
|
||||
Component: "memcached",
|
||||
Version: decidim.GetVersion(),
|
||||
})
|
||||
|
||||
svc.Spec.Ports = []corev1.ServicePort{
|
||||
{
|
||||
Port: MEMCACHED_PORT,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
TargetPort: intstr.FromInt(MEMCACHED_PORT),
|
||||
},
|
||||
}
|
||||
svc.Spec.Type = corev1.ServiceTypeClusterIP
|
||||
svc.Spec.Selector = lshr.ExtractLabelSelector(&svc)
|
||||
|
||||
return controllerutil.SetControllerReference(decidim, &svc, r.Scheme())
|
||||
})
|
||||
}
|
45
internal/controller/apps/decidim_controller_pdb.go
Normal file
45
internal/controller/apps/decidim_controller_pdb.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
policyv1 "k8s.io/api/policy/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) reconcilePodDisruptionBudget(ctx context.Context, decidim *appsv1alpha1.Decidim) error {
|
||||
var pdb policyv1.PodDisruptionBudget
|
||||
lshr.SetResourceNamespacedName(decidim, &pdb)
|
||||
return lshr.CreateOrPatch(ctx, r, &pdb, func() error {
|
||||
lshr.ApplyLabels(decidim, &pdb, nil)
|
||||
pdb.Spec.Selector = &v1.LabelSelector{
|
||||
MatchLabels: lshr.GetLabelSelector(decidim, &lshr.LabelOpts{
|
||||
Component: "app",
|
||||
}),
|
||||
}
|
||||
min := intstr.FromInt(1)
|
||||
pdb.Spec.MinAvailable = &min
|
||||
return controllerutil.SetControllerReference(decidim, &pdb, r.Scheme())
|
||||
})
|
||||
}
|
38
internal/controller/apps/decidim_controller_postgres.go
Normal file
38
internal/controller/apps/decidim_controller_postgres.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
corev1alpha1 "libre.sh/api/core/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) reconcilePostgres(ctx context.Context, decidim *appsv1alpha1.Decidim, resources *decidimResources) error {
|
||||
var postgres corev1alpha1.Postgres
|
||||
resources.postgres = &postgres
|
||||
lshr.SetResourceNamespacedName(decidim, &postgres)
|
||||
return lshr.CreateOrPatch(ctx, r, &postgres, func() error {
|
||||
lshr.ApplyLabels(decidim, &postgres, nil)
|
||||
postgres.Spec.Database = "decidim"
|
||||
return controllerutil.SetControllerReference(decidim, &postgres, r.Scheme())
|
||||
})
|
||||
}
|
40
internal/controller/apps/decidim_controller_redis.go
Normal file
40
internal/controller/apps/decidim_controller_redis.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshcore "libre.sh/api/core/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) reconcileRedis(ctx context.Context, decidim *appsv1alpha1.Decidim, resources *decidimResources) error {
|
||||
var redis lshcore.Redis
|
||||
resources.redis = &redis
|
||||
lshr.SetResourceNamespacedName(decidim, &redis)
|
||||
return lshr.CreateOrPatch(ctx, r, &redis, func() error {
|
||||
redis.Spec.Suspend = decidim.GetSuspend()
|
||||
redis.Spec.Persistence = lshcore.RedisPersistence{
|
||||
Enabled: true,
|
||||
}
|
||||
return controllerutil.SetControllerReference(decidim, &redis, r.Scheme())
|
||||
})
|
||||
}
|
56
internal/controller/apps/decidim_controller_svc.go
Normal file
56
internal/controller/apps/decidim_controller_svc.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshr "libre.sh/pkg/controller-runtime"
|
||||
)
|
||||
|
||||
func (r *DecidimReconciler) reconcileService(ctx context.Context, decidim *appsv1alpha1.Decidim) (*corev1.Service, error) {
|
||||
var svc corev1.Service
|
||||
lshr.SetResourceNamespacedName(decidim, &svc)
|
||||
err := lshr.CreateOrPatch(ctx, r, &svc, func() error {
|
||||
lshr.ApplyLabels(decidim, &svc, &lshr.LabelOpts{
|
||||
Component: "app",
|
||||
Version: decidim.GetVersion(),
|
||||
})
|
||||
targetPort := intstr.IntOrString{
|
||||
StrVal: "http",
|
||||
Type: intstr.String,
|
||||
}
|
||||
svc.Spec.Ports = []corev1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
TargetPort: targetPort,
|
||||
},
|
||||
}
|
||||
svc.Spec.Type = corev1.ServiceTypeClusterIP
|
||||
svc.Spec.Selector = lshr.ExtractLabelSelector(&svc)
|
||||
|
||||
return controllerutil.SetControllerReference(decidim, &svc, r.Scheme())
|
||||
})
|
||||
return &svc, err
|
||||
}
|
38
internal/decidim/health.go
Normal file
38
internal/decidim/health.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 decidim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
func HealthCheck(ctx context.Context, svc *corev1.Service) bool {
|
||||
log := log.FromContext(ctx)
|
||||
url := fmt.Sprintf("http://%s.%s.svc.cluster.local/health_check", svc.Name, svc.Namespace)
|
||||
resp, err := http.Get(url)
|
||||
if err == nil && resp.StatusCode == http.StatusOK {
|
||||
log.Info("Health check is ok")
|
||||
return true
|
||||
}
|
||||
log.Info("Waiting for health check to complete")
|
||||
return false
|
||||
}
|
168
internal/decidim/pg.go
Normal file
168
internal/decidim/pg.go
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
Copyright 2023 IndieHosters.
|
||||
|
||||
Licensed under the EUPL, Version 1.2 or later (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
|
||||
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 decidim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
_ "time/tzdata"
|
||||
|
||||
"github.com/lib/pq"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||
lshcore "libre.sh/api/core/v1alpha1"
|
||||
lshmeta "libre.sh/api/meta/v1alpha1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
func OpenPostgreSQL(ctx context.Context, c client.Client, postgres *lshcore.Postgres) (*sql.DB, error) {
|
||||
var secret corev1.Secret
|
||||
err := c.Get(ctx, types.NamespacedName{
|
||||
Namespace: postgres.Namespace,
|
||||
Name: postgres.SecretName(),
|
||||
}, &secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sql.Open("postgres", string(secret.Data[lshmeta.SecretURLKey]))
|
||||
}
|
||||
|
||||
func SyncOrganization(ctx context.Context, decidim *appsv1alpha1.Decidim, db *sql.DB) error {
|
||||
rows, err := db.Query(`SELECT count(*) from decidim_organizations WHERE id=$1`, decidim.Spec.Organization.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
var count int
|
||||
if rows.Next() {
|
||||
err = rows.Scan(&count)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.FromContext(ctx).Info("sql count", "value", count)
|
||||
kv := getRowKV(decidim)
|
||||
var args []interface{}
|
||||
var query string
|
||||
if count == 0 {
|
||||
query, args = getInsertQuery(decidim, kv)
|
||||
} else {
|
||||
query, args = getUpdateQuery(decidim, kv)
|
||||
}
|
||||
_, err = db.Exec(query, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
func getTZNow(decidim *appsv1alpha1.Decidim) time.Time {
|
||||
tz, err := time.LoadLocation(decidim.Spec.TimeZone)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return time.Now().In(tz)
|
||||
}
|
||||
|
||||
func getRowKV(decidim *appsv1alpha1.Decidim) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"name": decidim.Name,
|
||||
"host": decidim.Spec.Host,
|
||||
"default_locale": decidim.Spec.Locale.Default,
|
||||
"available_locales": pq.Array(decidim.Spec.Locale.Available),
|
||||
"updated_at": getTZNow(decidim),
|
||||
// "description": nil,
|
||||
// "logo": nil,
|
||||
// "twitter_handler": nil,
|
||||
// "show_statistics": true,
|
||||
// "favicon": nil,
|
||||
// "instagram_handler": nil,
|
||||
// "facebook_handler": nil,
|
||||
// "youtube_handler": nil,
|
||||
// "github_handler": nil,
|
||||
// "official_img_header": nil,
|
||||
// "official_img_footer": nil,
|
||||
// "official_url": nil,
|
||||
"reference_prefix": "/",
|
||||
"secondary_hosts": pq.Array(decidim.Spec.AdditionalHosts),
|
||||
"available_authorizations": pq.Array(decidim.Spec.AvailableAuthorizations),
|
||||
"file_upload_settings": decidim.Spec.FileUploadSettings.Raw,
|
||||
// "header_snippets": nil,
|
||||
// "cta_button_text": nil,
|
||||
// "cta_button_path": nil,
|
||||
// "enable_omnipresent_banner": false,
|
||||
// "omnipresent_banner_title": nil,
|
||||
// "omnipresent_banner_short_description": nil,
|
||||
// "omnipresent_banner_url": nil,
|
||||
// "highlighted_content_banner_enabled": false,
|
||||
// "highlighted_content_banner_title": nil,
|
||||
// "highlighted_content_banner_short_description": nil,
|
||||
// "highlighted_content_banner_action_title": nil,
|
||||
// "highlighted_content_banner_action_subtitle": nil,
|
||||
// "highlighted_content_banner_action_url": nil,
|
||||
// "highlighted_content_banner_image": nil,
|
||||
// "tos_version": nil,
|
||||
// "badges_enabled": nil,
|
||||
// "send_welcome_notification": nil,
|
||||
// "welcome_notification_subject": nil,
|
||||
// "welcome_notification_body": nil,
|
||||
"users_registration_mode": decidim.Spec.UsersRegistrationMode,
|
||||
// "id_documents_methods": nil,
|
||||
// "id_documents_explanation_text": nil,
|
||||
// "user_groups_enabled": nil,
|
||||
// "smtp_settings": nil,
|
||||
// "colors": nil,
|
||||
"force_users_to_authenticate_before_access_organization": decidim.Spec.ForceUsersToAuthenticateBeforeAccessOrganization,
|
||||
// "omniauth_settings": nil,
|
||||
// "rich_text_editor_in_public_views": nil,
|
||||
// "admin_terms_of_use_body": nil,
|
||||
"time_zone": decidim.Spec.TimeZone,
|
||||
// "deepl_api_key": nil,
|
||||
}
|
||||
}
|
||||
|
||||
func getInsertQuery(decidim *appsv1alpha1.Decidim, kv map[string]interface{}) (string, []interface{}) {
|
||||
kv["id"] = decidim.Spec.Organization.ID
|
||||
kv["created_at"] = getTZNow(decidim)
|
||||
columns := []string{}
|
||||
values := []string{}
|
||||
args := []interface{}{}
|
||||
i := 1
|
||||
for k, v := range kv {
|
||||
columns = append(columns, k)
|
||||
args = append(args, v)
|
||||
values = append(values, fmt.Sprintf("$%d", i))
|
||||
i++
|
||||
}
|
||||
return fmt.Sprintf("INSERT INTO decidim_organizations (%s) VALUES (%s)", strings.Join(columns, ","), strings.Join(values, ",")), args
|
||||
}
|
||||
|
||||
func getUpdateQuery(decidim *appsv1alpha1.Decidim, kv map[string]interface{}) (string, []interface{}) {
|
||||
args := []interface{}{}
|
||||
sets := []string{}
|
||||
i := 1
|
||||
for k, v := range kv {
|
||||
set := fmt.Sprintf("%s=$%d", k, i)
|
||||
sets = append(sets, set)
|
||||
args = append(args, v)
|
||||
i++
|
||||
}
|
||||
args = append(args, decidim.Spec.Organization.ID)
|
||||
return fmt.Sprintf("UPDATE decidim_organizations SET %s WHERE id=$%d", strings.Join(sets, ","), i), args
|
||||
}
|
Loading…
Reference in a new issue