mirror of
https://forge.liiib.re/indiehost/libre.sh/libre.sh.git
synced 2025-01-15 06:53:09 +00:00
This commit is contained in:
parent
d2452df888
commit
a590a16f2b
19 changed files with 922 additions and 1 deletions
9
PROJECT
9
PROJECT
|
@ -304,4 +304,13 @@ resources:
|
||||||
kind: Wikijs
|
kind: Wikijs
|
||||||
path: libre.sh/api/apps/v1alpha1
|
path: libre.sh/api/apps/v1alpha1
|
||||||
version: v1alpha1
|
version: v1alpha1
|
||||||
|
- api:
|
||||||
|
crdVersion: v1
|
||||||
|
namespaced: true
|
||||||
|
controller: true
|
||||||
|
domain: libre.sh
|
||||||
|
group: apps
|
||||||
|
kind: Vaultwarden
|
||||||
|
path: libre.sh/api/apps/v1alpha1
|
||||||
|
version: v1alpha1
|
||||||
version: "3"
|
version: "3"
|
||||||
|
|
67
api/apps/v1alpha1/vaultwarden_types.go
Normal file
67
api/apps/v1alpha1/vaultwarden_types.go
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
// SPDX-FileCopyrightText: 2025 IndieHosters <contact@indiehosters.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
lshmeta "libre.sh/api/meta/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VaultwardenSpec defines the desired state of Vaultwarden.
|
||||||
|
type VaultwardenSpec struct {
|
||||||
|
lshmeta.Spec `json:",inline"`
|
||||||
|
//+kubebuilder:validation:Required
|
||||||
|
//+kubebuilder:validation:MinLength=3
|
||||||
|
Host string `json:"host"`
|
||||||
|
//+kubebuilder:validation:Required
|
||||||
|
//+kubebuilder:validation:MinLength=3
|
||||||
|
Image string `json:"image"`
|
||||||
|
//+kubebuilder:validation:Required
|
||||||
|
MailboxRef corev1.LocalObjectReference `json:"mailboxRef"`
|
||||||
|
//+kubebuilder:validation:Type=object
|
||||||
|
//+kubebuilder:validation:Schemaless
|
||||||
|
//+kubebuilder:pruning:PreserveUnknownFields
|
||||||
|
Config json.RawMessage `json:"config,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VaultwardenStatus defines the observed state of Vaultwarden.
|
||||||
|
type VaultwardenStatus struct {
|
||||||
|
lshmeta.Status `json:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate lsh-gen v1alpha1 Vaultwarden
|
||||||
|
|
||||||
|
//+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=""
|
||||||
|
|
||||||
|
// Vaultwarden is the Schema for the vaultwardens API.
|
||||||
|
type Vaultwarden struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec VaultwardenSpec `json:"spec,omitempty"`
|
||||||
|
Status VaultwardenStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// VaultwardenList contains a list of Vaultwarden.
|
||||||
|
type VaultwardenList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []Vaultwarden `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&Vaultwarden{}, &VaultwardenList{})
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
//go:build !ignore_autogenerated
|
//go:build !ignore_autogenerated
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2024 - 2025 IndieHosters <contact@indiehosters.net>
|
// SPDX-FileCopyrightText: 2025 IndieHosters <contact@indiehosters.net>
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: EUPL-1.2
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
@ -1265,6 +1265,103 @@ func (in *StaticWebsiteStatus) DeepCopy() *StaticWebsiteStatus {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Vaultwarden) DeepCopyInto(out *Vaultwarden) {
|
||||||
|
*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 Vaultwarden.
|
||||||
|
func (in *Vaultwarden) DeepCopy() *Vaultwarden {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Vaultwarden)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *Vaultwarden) 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 *VaultwardenList) DeepCopyInto(out *VaultwardenList) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]Vaultwarden, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultwardenList.
|
||||||
|
func (in *VaultwardenList) DeepCopy() *VaultwardenList {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(VaultwardenList)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *VaultwardenList) 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 *VaultwardenSpec) DeepCopyInto(out *VaultwardenSpec) {
|
||||||
|
*out = *in
|
||||||
|
out.Spec = in.Spec
|
||||||
|
out.MailboxRef = in.MailboxRef
|
||||||
|
if in.Config != nil {
|
||||||
|
in, out := &in.Config, &out.Config
|
||||||
|
*out = make(json.RawMessage, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultwardenSpec.
|
||||||
|
func (in *VaultwardenSpec) DeepCopy() *VaultwardenSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(VaultwardenSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *VaultwardenStatus) DeepCopyInto(out *VaultwardenStatus) {
|
||||||
|
*out = *in
|
||||||
|
in.Status.DeepCopyInto(&out.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultwardenStatus.
|
||||||
|
func (in *VaultwardenStatus) DeepCopy() *VaultwardenStatus {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(VaultwardenStatus)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Wikijs) DeepCopyInto(out *Wikijs) {
|
func (in *Wikijs) DeepCopyInto(out *Wikijs) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|
39
api/apps/v1alpha1/zz_generated.vaultwarden_lsh.go
Normal file
39
api/apps/v1alpha1/zz_generated.vaultwarden_lsh.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// SPDX-FileCopyrightText: 2025 IndieHosters <contact@indiehosters.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
// Code generated by lsh-gen; DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (o *Vaultwarden) GetSuspend() bool {
|
||||||
|
return o.Spec.Suspend
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Vaultwarden) SetSuspend(value bool) {
|
||||||
|
o.Spec.Suspend = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Vaultwarden) GetVersion() string {
|
||||||
|
return o.Status.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Vaultwarden) SetVersion(value string) {
|
||||||
|
o.Status.Version = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Vaultwarden) GetImage() string {
|
||||||
|
return o.Spec.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Vaultwarden) GetConditions() []metav1.Condition {
|
||||||
|
return o.Status.Conditions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Vaultwarden) SetConditions(conditions []metav1.Condition) {
|
||||||
|
o.Status.Conditions = conditions
|
||||||
|
}
|
|
@ -354,6 +354,12 @@ func main() {
|
||||||
setupLog.Error(err, "unable to create controller", "controller", "Wikijs")
|
setupLog.Error(err, "unable to create controller", "controller", "Wikijs")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
if err = (&appscontroller.VaultwardenReconciler{
|
||||||
|
Client: mgr.GetClient(),
|
||||||
|
}).SetupWithManager(mgr); err != nil {
|
||||||
|
setupLog.Error(err, "unable to create controller", "controller", "Vaultwarden")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
//+kubebuilder:scaffold:builder
|
//+kubebuilder:scaffold:builder
|
||||||
|
|
||||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||||
|
|
159
config/crd/bases/apps.libre.sh_vaultwardens.yaml
Normal file
159
config/crd/bases/apps.libre.sh_vaultwardens.yaml
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.14.0
|
||||||
|
name: vaultwardens.apps.libre.sh
|
||||||
|
spec:
|
||||||
|
group: apps.libre.sh
|
||||||
|
names:
|
||||||
|
kind: Vaultwarden
|
||||||
|
listKind: VaultwardenList
|
||||||
|
plural: vaultwardens
|
||||||
|
singular: vaultwarden
|
||||||
|
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: Vaultwarden is the Schema for the vaultwardens 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: VaultwardenSpec defines the desired state of Vaultwarden.
|
||||||
|
properties:
|
||||||
|
config:
|
||||||
|
type: object
|
||||||
|
x-kubernetes-preserve-unknown-fields: true
|
||||||
|
host:
|
||||||
|
minLength: 3
|
||||||
|
type: string
|
||||||
|
image:
|
||||||
|
minLength: 3
|
||||||
|
type: string
|
||||||
|
mailboxRef:
|
||||||
|
description: |-
|
||||||
|
LocalObjectReference contains enough information to let you locate the
|
||||||
|
referenced object inside the same namespace.
|
||||||
|
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
|
||||||
|
type: object
|
||||||
|
x-kubernetes-map-type: atomic
|
||||||
|
suspend:
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- host
|
||||||
|
- image
|
||||||
|
- mailboxRef
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: VaultwardenStatus defines the observed state of Vaultwarden.
|
||||||
|
properties:
|
||||||
|
conditions:
|
||||||
|
items:
|
||||||
|
description: "Condition contains details for one aspect of the current state of this API Resource.\n---\nThis struct is intended for direct use as an array at the field path .status.conditions. For example,\n\n\n\ttype FooStatus struct{\n\t // Represents the observations of a foo's current state.\n\t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t // other fields\n\t}"
|
||||||
|
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: {}
|
|
@ -35,6 +35,7 @@ resources:
|
||||||
- bases/apps.libre.sh_listmonks.yaml
|
- bases/apps.libre.sh_listmonks.yaml
|
||||||
- bases/apps.libre.sh_staticwebsites.yaml
|
- bases/apps.libre.sh_staticwebsites.yaml
|
||||||
- bases/apps.libre.sh_wikijs.yaml
|
- bases/apps.libre.sh_wikijs.yaml
|
||||||
|
- bases/apps.libre.sh_vaultwardens.yaml
|
||||||
#+kubebuilder:scaffold:crdkustomizeresource
|
#+kubebuilder:scaffold:crdkustomizeresource
|
||||||
patches:
|
patches:
|
||||||
|
|
||||||
|
|
27
config/rbac/apps_vaultwarden_editor_role.yaml
Normal file
27
config/rbac/apps_vaultwarden_editor_role.yaml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# permissions for end users to edit vaultwardens.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: libre-sh
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: apps-vaultwarden-editor-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- apps.libre.sh
|
||||||
|
resources:
|
||||||
|
- vaultwardens
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- apps.libre.sh
|
||||||
|
resources:
|
||||||
|
- vaultwardens/status
|
||||||
|
verbs:
|
||||||
|
- get
|
23
config/rbac/apps_vaultwarden_viewer_role.yaml
Normal file
23
config/rbac/apps_vaultwarden_viewer_role.yaml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# permissions for end users to view vaultwardens.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: libre-sh
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: apps-vaultwarden-viewer-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- apps.libre.sh
|
||||||
|
resources:
|
||||||
|
- vaultwardens
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- apps.libre.sh
|
||||||
|
resources:
|
||||||
|
- vaultwardens/status
|
||||||
|
verbs:
|
||||||
|
- get
|
|
@ -30,3 +30,5 @@ resources:
|
||||||
- matrix_elementcall_viewer_role.yaml
|
- matrix_elementcall_viewer_role.yaml
|
||||||
- voip_livekitserver_editor_role.yaml
|
- voip_livekitserver_editor_role.yaml
|
||||||
- voip_livekitserver_viewer_role.yaml
|
- voip_livekitserver_viewer_role.yaml
|
||||||
|
- apps_vaultwarden_viewer_role.yaml
|
||||||
|
- apps_vaultwarden_editor_role.yaml
|
||||||
|
|
|
@ -404,6 +404,32 @@ rules:
|
||||||
- get
|
- get
|
||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- apps.libre.sh
|
||||||
|
resources:
|
||||||
|
- vaultwardens
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- apps.libre.sh
|
||||||
|
resources:
|
||||||
|
- vaultwardens/finalizers
|
||||||
|
verbs:
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- apps.libre.sh
|
||||||
|
resources:
|
||||||
|
- vaultwardens/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- apps.libre.sh
|
- apps.libre.sh
|
||||||
resources:
|
resources:
|
||||||
|
|
13
config/samples/apps_v1alpha1_vaultwarden.yaml
Normal file
13
config/samples/apps_v1alpha1_vaultwarden.yaml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
apiVersion: apps.libre.sh/v1alpha1
|
||||||
|
kind: Vaultwarden
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: libre-sh
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: vaultwarden-sample
|
||||||
|
spec:
|
||||||
|
host: vault.dev.local
|
||||||
|
image: docker.io/vaultwarden/server:latest
|
||||||
|
mailboxRef:
|
||||||
|
name: mailbox-sample
|
||||||
|
config: {}
|
|
@ -33,4 +33,5 @@ resources:
|
||||||
- apps_v1alpha1_listmonk.yaml
|
- apps_v1alpha1_listmonk.yaml
|
||||||
- apps_v1alpha1_staticwebsite.yaml
|
- apps_v1alpha1_staticwebsite.yaml
|
||||||
- apps_v1alpha1_wikijs.yaml
|
- apps_v1alpha1_wikijs.yaml
|
||||||
|
- apps_v1alpha1_vaultwarden.yaml
|
||||||
#+kubebuilder:scaffold:manifestskustomizesamples
|
#+kubebuilder:scaffold:manifestskustomizesamples
|
||||||
|
|
128
internal/controller/apps/vaultwarden_controller.go
Normal file
128
internal/controller/apps/vaultwarden_controller.go
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
// SPDX-FileCopyrightText: 2025 IndieHosters <contact@indiehosters.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
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"
|
||||||
|
lshr "libre.sh/pkg/controller-runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type vaultwardenResources struct {
|
||||||
|
postgres *lshcore.Postgres
|
||||||
|
pvc *corev1.PersistentVolumeClaim
|
||||||
|
mailbox *lshcore.Mailbox
|
||||||
|
secret *corev1.Secret
|
||||||
|
}
|
||||||
|
|
||||||
|
// VaultwardenReconciler reconciles a Vaultwarden object
|
||||||
|
type VaultwardenReconciler struct {
|
||||||
|
client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:rbac:groups=apps.libre.sh,resources=vaultwardens,verbs=get;list;watch;create;update;patch;delete
|
||||||
|
// +kubebuilder:rbac:groups=apps.libre.sh,resources=vaultwardens/status,verbs=get;update;patch
|
||||||
|
// +kubebuilder:rbac:groups=apps.libre.sh,resources=vaultwardens/finalizers,verbs=update
|
||||||
|
|
||||||
|
func (r *VaultwardenReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
log := log.FromContext(ctx)
|
||||||
|
|
||||||
|
var vault appsv1alpha1.Vaultwarden
|
||||||
|
patcher, result := lshr.Initialize(ctx, r, req, &vault)
|
||||||
|
if result != nil {
|
||||||
|
return result.Unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
if lshr.IsFinalizing(&vault) {
|
||||||
|
return lshr.Finalize(ctx, r, patcher, &vault, func() error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resources := vaultwardenResources{}
|
||||||
|
|
||||||
|
_, err := lshr.ReconcileGenericIngress(ctx, r, &vault, vault.Spec.Host, nil)
|
||||||
|
if err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resources.postgres, err = lshr.ReconcileGenericPostgres(ctx, r, &vault, "vaultwarden")
|
||||||
|
if err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.reconcilePVC(ctx, &vault, &resources)
|
||||||
|
if err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resources.mailbox = &lshcore.Mailbox{}
|
||||||
|
resources.mailbox.Namespace = vault.Namespace
|
||||||
|
resources.mailbox.Name = vault.Spec.MailboxRef.Name
|
||||||
|
err = r.Get(ctx, client.ObjectKeyFromObject(resources.mailbox), resources.mailbox)
|
||||||
|
if err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lshr.SetDependencyCondition(&vault, resources.postgres, resources.mailbox)
|
||||||
|
if err := lshr.Patch(ctx, r, patcher, &vault, lshr.PatchOpts{}); err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
if lshr.IsDependencyNotReady(&vault) || lshr.IsImporting(&vault) {
|
||||||
|
log.Info("Waiting for dependencies or importation")
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = lshr.ReconcileGenericService(ctx, r, &vault)
|
||||||
|
if err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.reconcileSecret(ctx, &vault, &resources)
|
||||||
|
if err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.reconcileDeployment(ctx, &vault, &resources)
|
||||||
|
if err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctrl.Result{}, lshr.Complete(ctx, r, patcher, &vault, lshr.PatchOpts{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
|
func (r *VaultwardenReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&appsv1alpha1.Vaultwarden{}).
|
||||||
|
Named(r.Name()).
|
||||||
|
Owns(&lshcore.Postgres{}).
|
||||||
|
Owns(&appsv1.Deployment{}).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *VaultwardenReconciler) Name() string {
|
||||||
|
return "vaultwarden-controller"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *VaultwardenReconciler) OwnedConditions() []string {
|
||||||
|
return []string{
|
||||||
|
meta.ReconcilingCondition,
|
||||||
|
meta.StalledCondition,
|
||||||
|
lshmeta.DependenciesNotReady,
|
||||||
|
lshmeta.HookJobNotComleted,
|
||||||
|
}
|
||||||
|
}
|
116
internal/controller/apps/vaultwarden_controller_deploy.go
Normal file
116
internal/controller/apps/vaultwarden_controller_deploy.go
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
// Copyright 2024 IndieHosters.
|
||||||
|
// SPDX-FileCopyrightText: 2025 IndieHosters <contact@indiehosters.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
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 *VaultwardenReconciler) reconcileDeployment(ctx context.Context, vault *appsv1alpha1.Vaultwarden, resources *vaultwardenResources) error {
|
||||||
|
var deploy appsv1.Deployment
|
||||||
|
lshr.SetResourceNamespacedName(vault, &deploy)
|
||||||
|
return lshr.CreateOrPatch(ctx, r, &deploy, func() error {
|
||||||
|
lshr.ApplyLabels(vault, &deploy, nil)
|
||||||
|
|
||||||
|
deploy.Spec.Selector = &metav1.LabelSelector{
|
||||||
|
MatchLabels: lshr.ExtractLabelSelector(&deploy),
|
||||||
|
}
|
||||||
|
deploy.Spec.Template.ObjectMeta.Labels = deploy.Labels
|
||||||
|
deploy.Spec.Template.Spec.TopologySpreadConstraints = []corev1.TopologySpreadConstraint{
|
||||||
|
{
|
||||||
|
MaxSkew: 1,
|
||||||
|
TopologyKey: "kubernetes.io/hostname",
|
||||||
|
WhenUnsatisfiable: corev1.ScheduleAnyway,
|
||||||
|
LabelSelector: deploy.Spec.Selector,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
deploy.Spec.Strategy.Type = appsv1.RollingUpdateDeploymentStrategyType
|
||||||
|
if deploy.Spec.Replicas == nil {
|
||||||
|
replicas := int32(2)
|
||||||
|
deploy.Spec.Replicas = &replicas
|
||||||
|
}
|
||||||
|
|
||||||
|
deploy.Spec.Template.Spec.Volumes = []corev1.Volume{
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
|
||||||
|
ClaimName: resources.pvc.GetName(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "config",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
Secret: &corev1.SecretVolumeSource{
|
||||||
|
SecretName: lshr.GetResourceName(vault),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
probeHandler := corev1.ProbeHandler{
|
||||||
|
HTTPGet: &corev1.HTTPGetAction{
|
||||||
|
Path: "/alive",
|
||||||
|
Port: intstr.FromString("http"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mountPropagation := corev1.MountPropagationHostToContainer
|
||||||
|
deploy.Spec.Template.Spec.Containers = []corev1.Container{
|
||||||
|
{
|
||||||
|
Image: vault.Spec.Image,
|
||||||
|
ImagePullPolicy: corev1.PullAlways,
|
||||||
|
Name: "app",
|
||||||
|
Ports: []corev1.ContainerPort{
|
||||||
|
{
|
||||||
|
ContainerPort: 80,
|
||||||
|
Name: "http",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ReadinessProbe: &corev1.Probe{
|
||||||
|
ProbeHandler: probeHandler,
|
||||||
|
},
|
||||||
|
LivenessProbe: &corev1.Probe{
|
||||||
|
ProbeHandler: probeHandler,
|
||||||
|
},
|
||||||
|
VolumeMounts: []corev1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "data",
|
||||||
|
MountPath: "/data",
|
||||||
|
MountPropagation: &mountPropagation,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "config",
|
||||||
|
MountPath: "/data/config.json",
|
||||||
|
SubPath: "config.json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Resources: corev1.ResourceRequirements{
|
||||||
|
Requests: corev1.ResourceList{
|
||||||
|
corev1.ResourceCPU: resource.MustParse("50m"),
|
||||||
|
corev1.ResourceMemory: resource.MustParse("100Mi"),
|
||||||
|
},
|
||||||
|
Limits: corev1.ResourceList{
|
||||||
|
corev1.ResourceMemory: resource.MustParse("500Mi"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return controllerutil.SetControllerReference(vault, &deploy, r.Scheme())
|
||||||
|
})
|
||||||
|
}
|
36
internal/controller/apps/vaultwarden_controller_pvc.go
Normal file
36
internal/controller/apps/vaultwarden_controller_pvc.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 - 2025 IndieHosters <contact@indiehosters.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
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 *VaultwardenReconciler) reconcilePVC(ctx context.Context, vault *appsv1alpha1.Vaultwarden, resources *vaultwardenResources) error {
|
||||||
|
var pvc corev1.PersistentVolumeClaim
|
||||||
|
resources.pvc = &pvc
|
||||||
|
lshr.SetResourceNamespacedName(vault, &pvc)
|
||||||
|
return lshr.CreateOrPatch(ctx, r, &pvc, func() error {
|
||||||
|
lshr.ApplyLabels(vault, &pvc, nil)
|
||||||
|
pvc.Spec.AccessModes = []corev1.PersistentVolumeAccessMode{
|
||||||
|
corev1.ReadWriteMany,
|
||||||
|
}
|
||||||
|
storageClassName := "juicefs.libre.sh"
|
||||||
|
pvc.Spec.StorageClassName = &storageClassName
|
||||||
|
pvc.Spec.Resources = corev1.ResourceRequirements{
|
||||||
|
Requests: corev1.ResourceList{
|
||||||
|
corev1.ResourceStorage: resource.MustParse("10Gi"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return controllerutil.SetControllerReference(vault, &pvc, r.Scheme())
|
||||||
|
})
|
||||||
|
}
|
30
internal/controller/apps/vaultwarden_controller_secret.cue
Normal file
30
internal/controller/apps/vaultwarden_controller_secret.cue
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// SPDX-FileCopyrightText: 2025 IndieHosters <contact@indiehosters.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
_vault: {}
|
||||||
|
_postgres: {}
|
||||||
|
_mailbox: {}
|
||||||
|
|
||||||
|
domain: "https://\(_vault.spec.host)"
|
||||||
|
database_url: strings.Replace("\(_postgres.url)", "postgres://", "postgresql://", 1)
|
||||||
|
smtp_from: "\(_mailbox.address)"
|
||||||
|
smtp_host: "\(_mailbox.host)"
|
||||||
|
smtp_port: strconv.Atoi("\(_mailbox.port)")
|
||||||
|
smtp_username: "\(_mailbox.username)"
|
||||||
|
smtp_password: "\(_mailbox.password)"
|
||||||
|
smtp_security: string | *"off"
|
||||||
|
if strconv.ParseBool("\(_mailbox.tls)") {
|
||||||
|
smtp_security: "starttls"
|
||||||
|
}
|
||||||
|
|
||||||
|
if _vault.spec.config != _|_ {
|
||||||
|
for k, v in _vault.spec.config {
|
||||||
|
"\(k)": v
|
||||||
|
}
|
||||||
|
}
|
70
internal/controller/apps/vaultwarden_controller_secret.go
Normal file
70
internal/controller/apps/vaultwarden_controller_secret.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 - 2025 IndieHosters <contact@indiehosters.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
|
||||||
|
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||||
|
"libre.sh/internal/cue"
|
||||||
|
lshr "libre.sh/pkg/controller-runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed vaultwarden_controller_secret.cue
|
||||||
|
var vaultwardenConfigSchema string
|
||||||
|
|
||||||
|
func (r *VaultwardenReconciler) reconcileSecret(ctx context.Context, vault *appsv1alpha1.Vaultwarden, resources *vaultwardenResources) error {
|
||||||
|
var secret corev1.Secret
|
||||||
|
resources.secret = &secret
|
||||||
|
lshr.SetResourceNamespacedName(vault, &secret)
|
||||||
|
return lshr.CreateOrPatch(ctx, r, &secret, func() error {
|
||||||
|
if secret.Data == nil {
|
||||||
|
secret.Data = make(map[string][]byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pgSecret corev1.Secret
|
||||||
|
err := r.Get(ctx, types.NamespacedName{
|
||||||
|
Namespace: vault.Namespace,
|
||||||
|
Name: resources.postgres.SecretName(),
|
||||||
|
}, &pgSecret)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var mailboxSecret corev1.Secret
|
||||||
|
err = r.Get(ctx, types.NamespacedName{
|
||||||
|
Namespace: vault.Namespace,
|
||||||
|
Name: resources.mailbox.SecretName(),
|
||||||
|
}, &mailboxSecret)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
config, _, err := cue.GenerateConfig(vaultwardenConfigSchema, cue.FromJSON(secret.Data["config.json"]), cue.ToJSON(),
|
||||||
|
cue.WithValue("_vault", vault),
|
||||||
|
cue.WithValue("_postgres", pgSecret.Data),
|
||||||
|
cue.WithValue("_mailbox", mailboxSecret.Data),
|
||||||
|
// cue.WithGenerator("forgejo-internal-token", func() interface{} {
|
||||||
|
// value, _ := generate.NewInternalToken()
|
||||||
|
// return value
|
||||||
|
// }),
|
||||||
|
// cue.WithGenerator("forgejo-jwt-secret", func() interface{} {
|
||||||
|
// _, value, _ := generate.NewJwtSecret()
|
||||||
|
// return value
|
||||||
|
// }),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
secret.Data["config.json"] = []byte(config)
|
||||||
|
|
||||||
|
return controllerutil.SetControllerReference(vault, &secret, r.Scheme())
|
||||||
|
})
|
||||||
|
}
|
71
internal/controller/apps/vaultwarden_controller_test.go
Normal file
71
internal/controller/apps/vaultwarden_controller_test.go
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
// SPDX-FileCopyrightText: 2025 IndieHosters <contact@indiehosters.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
appsv1alpha1 "libre.sh/api/apps/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Vaultwarden Controller", func() {
|
||||||
|
Context("When reconciling a resource", func() {
|
||||||
|
const resourceName = "test-resource"
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
typeNamespacedName := types.NamespacedName{
|
||||||
|
Name: resourceName,
|
||||||
|
Namespace: "default", // TODO(user):Modify as needed
|
||||||
|
}
|
||||||
|
vaultwarden := &appsv1alpha1.Vaultwarden{}
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
By("creating the custom resource for the Kind Vaultwarden")
|
||||||
|
err := k8sClient.Get(ctx, typeNamespacedName, vaultwarden)
|
||||||
|
if err != nil && errors.IsNotFound(err) {
|
||||||
|
resource := &appsv1alpha1.Vaultwarden{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: resourceName,
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
// TODO(user): Specify other spec details if needed.
|
||||||
|
}
|
||||||
|
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
AfterEach(func() {
|
||||||
|
// TODO(user): Cleanup logic after each test, like removing the resource instance.
|
||||||
|
resource := &appsv1alpha1.Vaultwarden{}
|
||||||
|
err := k8sClient.Get(ctx, typeNamespacedName, resource)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
By("Cleanup the specific resource instance Vaultwarden")
|
||||||
|
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
|
||||||
|
})
|
||||||
|
It("should successfully reconcile the resource", func() {
|
||||||
|
By("Reconciling the created resource")
|
||||||
|
controllerReconciler := &VaultwardenReconciler{
|
||||||
|
Client: k8sClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
|
||||||
|
// Example: If you expect a certain status condition after reconciliation, verify it here.
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in a new issue