Skip to content

Commit 4c835cd

Browse files
AleToppkingmakerbot
authored andcommitted
Add NFS Shared Volumes (backend)
1 parent f79aa41 commit 4c835cd

36 files changed

Lines changed: 2176 additions & 97 deletions

deploy/crownlabs/templates/clusterroles.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,3 +403,25 @@ rules:
403403
- list
404404
- watch
405405
{{- end }}
406+
407+
---
408+
apiVersion: rbac.authorization.k8s.io/v1
409+
kind: ClusterRole
410+
metadata:
411+
name: crownlabs-manage-sharedvolumes
412+
labels:
413+
{{- include "crownlabs.labels" . | nindent 4 }}
414+
rules:
415+
- apiGroups:
416+
- crownlabs.polito.it
417+
resources:
418+
- sharedvolumes
419+
verbs:
420+
- get
421+
- list
422+
- watch
423+
- create
424+
- update
425+
- patch
426+
- delete
427+
- deletecollection

infrastructure/certificate-provisioning/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ By default, cert-manager has been configured to prove the control of the domain
111111
labels:
112112
use-dns01-solver: "true"
113113
```
114-
115114
## Synchronize digital certificates between namespaces
116115
❗❗ `Kubed is no longer available and has been superseded by ConfigSyncer`
117116

operators/api/v1alpha2/common.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ const (
4949
SubscrFailed SubscriptionStatus = "Failed"
5050
)
5151

52+
// TemplateLabelPrefix is the prefix of a label assigned to a sharedvolume indicating it is mounted on a template.
53+
const TemplateLabelPrefix = "crownlabs.polito.it/template-"
54+
5255
// WorkspaceLabelPrefix is the prefix of a label assigned to a tenant indicating it is subscribed to a workspace.
5356
const WorkspaceLabelPrefix = "crownlabs.polito.it/workspace-"
5457

@@ -57,3 +60,6 @@ const WorkspaceLabelAutoenroll = "crownlabs.polito.it/autoenroll"
5760

5861
// TnOperatorFinalizerName is the name of the finalizer corresponding to the tenant operator.
5962
const TnOperatorFinalizerName = "crownlabs.polito.it/tenant-operator"
63+
64+
// ShVolCtrlFinalizerName is the name of the finalizer for SharedVolume's PVC protection.
65+
const ShVolCtrlFinalizerName = "crownlabs.polito.it/shvolctrl-volume-protection"
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright 2020-2025 Politecnico di Torino
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package v1alpha2
16+
17+
import (
18+
"k8s.io/apimachinery/pkg/api/resource"
19+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20+
)
21+
22+
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
23+
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
24+
25+
// +kubebuilder:validation:Enum="";"Pending";"Provisioning";"Ready";"Deleting";"ResourceQuotaExceeded";"Error"
26+
27+
// SharedVolumePhase is an enumeration of the different phases associated with a SharedVolume.
28+
type SharedVolumePhase string
29+
30+
const (
31+
// SharedVolumePhaseUnset -> the shared volume phase is unknown.
32+
SharedVolumePhaseUnset SharedVolumePhase = ""
33+
// SharedVolumePhasePending -> the shared volume is pending.
34+
SharedVolumePhasePending SharedVolumePhase = "Pending"
35+
// SharedVolumePhaseProvisioning -> the shared volume's PVC is under provisioning.
36+
SharedVolumePhaseProvisioning SharedVolumePhase = "Provisioning"
37+
// SharedVolumePhaseReady -> the shared volume is bound and ready to be accessed.
38+
SharedVolumePhaseReady SharedVolumePhase = "Ready"
39+
// SharedVolumePhaseDeleting -> the shared volume is checking if it's mounted on any template before going through deletion.
40+
SharedVolumePhaseDeleting SharedVolumePhase = "Deleting"
41+
// SharedVolumePhaseResourceQuotaExceeded -> the shared volume could not be created because the resource quota is exceeded.
42+
SharedVolumePhaseResourceQuotaExceeded SharedVolumePhase = "ResourceQuotaExceeded"
43+
// SharedVolumePhaseError -> the shared volume had an error during reconcile.
44+
SharedVolumePhaseError SharedVolumePhase = "Error"
45+
)
46+
47+
// SharedVolumeSpec is the specification of the desired state of the Shared Volume.
48+
type SharedVolumeSpec struct {
49+
// The human-readable name of the Shared Volume.
50+
PrettyName string `json:"prettyName"`
51+
52+
// The size of the volume.
53+
Size resource.Quantity `json:"size"`
54+
}
55+
56+
// SharedVolumeStatus reflects the most recently observed status of the Shared Volume.
57+
type SharedVolumeStatus struct {
58+
// The NFS server address.
59+
ServerAddress string `json:"serverAddress,omitempty"`
60+
61+
// The NFS path.
62+
ExportPath string `json:"exportPath,omitempty"`
63+
64+
// The current phase of the lifecycle of the Shared Volume.
65+
Phase SharedVolumePhase `json:"phase,omitempty"`
66+
}
67+
68+
// +kubebuilder:object:root=true
69+
// +kubebuilder:subresource:status
70+
// +kubebuilder:resource:shortName="shvol"
71+
// +kubebuilder:storageversion
72+
// +kubebuilder:printcolumn:name="Pretty Name",type=string,JSONPath=`.spec.prettyName`
73+
// +kubebuilder:printcolumn:name="Size",type=string,JSONPath=`.spec.size`
74+
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
75+
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
76+
77+
// SharedVolume describes a shared volume between tenants in CrownLabs.
78+
type SharedVolume struct {
79+
metav1.TypeMeta `json:",inline"`
80+
metav1.ObjectMeta `json:"metadata,omitempty"`
81+
82+
Spec SharedVolumeSpec `json:"spec,omitempty"`
83+
Status SharedVolumeStatus `json:"status,omitempty"`
84+
}
85+
86+
// +kubebuilder:object:root=true
87+
88+
// SharedVolumeList contains a list of SharedVolume objects.
89+
type SharedVolumeList struct {
90+
metav1.TypeMeta `json:",inline"`
91+
metav1.ListMeta `json:"metadata,omitempty"`
92+
93+
Items []SharedVolume `json:"items"`
94+
}
95+
96+
func init() {
97+
SchemeBuilder.Register(&SharedVolume{}, &SharedVolumeList{})
98+
}

operators/api/v1alpha2/template_types.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ type Environment struct {
126126
// +kubebuilder:default=true
127127
// Whether the instance has to have the user's MyDrive volume
128128
MountMyDriveVolume bool `json:"mountMyDriveVolume"`
129+
130+
// The list of information about Shared Volumes that has to be mounted to the instance.
131+
SharedVolumeMounts []SharedVolumeMountInfo `json:"sharedVolumeMounts,omitempty"`
129132
}
130133

131134
// EnvironmentResources is the specification of the amount of resources
@@ -175,6 +178,18 @@ type ContainerStartupOpts struct {
175178
EnforceWorkdir bool `json:"enforceWorkdir"`
176179
}
177180

181+
// SharedVolumeMountInfo contains mount information for a Shared Volume.
182+
type SharedVolumeMountInfo struct {
183+
// The reference of the Shared Volume this Mount Info is related to.
184+
SharedVolumeRef GenericRef `json:"sharedVolume"`
185+
186+
// The path the Shared Volume will be mounted in.
187+
MountPath string `json:"mountPath"`
188+
189+
// Whether this Shared Volume should be mounted with R/W or R/O permission.
190+
ReadOnly bool `json:"readOnly"`
191+
}
192+
178193
// +kubebuilder:object:root=true
179194
// +kubebuilder:subresource:status
180195
// +kubebuilder:resource:shortName="tmpl"

operators/api/v1alpha2/zz_generated.deepcopy.go

Lines changed: 111 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

operators/cmd/instance-operator/main.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040
instancesnapshot_controller "github.com/netgroup-polito/CrownLabs/operators/pkg/instancesnapshot-controller"
4141
"github.com/netgroup-polito/CrownLabs/operators/pkg/instautoctrl"
4242
"github.com/netgroup-polito/CrownLabs/operators/pkg/instctrl"
43+
"github.com/netgroup-polito/CrownLabs/operators/pkg/shvolctrl"
4344
"github.com/netgroup-polito/CrownLabs/operators/pkg/utils/restcfg"
4445
)
4546

@@ -71,6 +72,8 @@ func main() {
7172
"which the controller will work. Different labels (key=value) can be specified, by separating them with a &"+
7273
"( e.g. key1=value1&key2=value2")
7374

75+
sharedVolumeStorageClass := flag.String("shared-volume-storage-class", "rook-nfs", "The StorageClass to be used for all SharedVolumes' PVC (if unique can be used to enforce ResourceQuota on Workspaces, about number and size of ShVols)")
76+
7477
maxConcurrentTerminationReconciles := flag.Int("max-concurrent-reconciles-termination", 1, "The maximum number of concurrent Reconciles which can be run for the Instance Termination controller")
7578
instanceTerminationStatusCheckTimeout := flag.Duration("instance-termination-status-check-timeout", 3*time.Second, "The maximum time to wait for the status check for Instances that require it")
7679
instanceTerminationStatusCheckInterval := flag.Duration("instance-termination-status-check-interval", 2*time.Minute, "The interval to check the status of Instances that require it")
@@ -173,6 +176,18 @@ func main() {
173176
os.Exit(1)
174177
}
175178

179+
// Configure the SharedVolume controller
180+
const sharedVolumeCtrl = "SharedVolume"
181+
if err := (&shvolctrl.SharedVolumeReconciler{
182+
Client: mgr.GetClient(),
183+
EventsRecorder: mgr.GetEventRecorderFor(sharedVolumeCtrl),
184+
NamespaceWhitelist: nsWhitelist,
185+
PVCStorageClass: *sharedVolumeStorageClass,
186+
}).SetupWithManager(mgr, *maxConcurrentSubmissionReconciles); err != nil {
187+
log.Error(err, "unable to create controller", "controller", sharedVolumeCtrl)
188+
os.Exit(1)
189+
}
190+
176191
// Add readiness probe
177192
err = mgr.AddReadyzCheck("ready-ping", healthz.Ping)
178193
if err != nil {

0 commit comments

Comments
 (0)