/
calculator.go
323 lines (272 loc) · 10.6 KB
/
calculator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/*
Copyright The CloudNativePG Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package persistentvolumeclaim
import (
"fmt"
volumesnapshot "github.com/kubernetes-csi/external-snapshotter/client/v7/apis/volumesnapshot/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/utils/ptr"
apiv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
"github.com/cloudnative-pg/cloudnative-pg/pkg/specs"
"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
)
// Meta is an object capable of describing the metadata of a pvc
type Meta interface {
// GetName will be used to get the name of the PVC
GetName(instanceName string) string
// GetLabels will be used as the label value
GetLabels(instanceName string) map[string]string
// GetRoleName return the role name in string
GetRoleName() string
}
// Bootstrap is an object capable of describing the starting status of a pvc
type Bootstrap interface {
// GetInitialStatus returns the status the PVC should be first created with
GetInitialStatus() PVCStatus
}
// Backup is an object capable of describing the backup behaviour of a pvc
type Backup interface {
// GetSnapshotName gets the snapshot name for a certain PVC
GetSnapshotName(backupName string) string
// GetVolumeSnapshotClass will return the volume snapshot class to be used
// when snapshotting a PVC with this Role.
GetVolumeSnapshotClass(configuration *apiv1.VolumeSnapshotConfiguration) *string
}
// Configuration is an object capable of describing the configuration of a pvc
type Configuration interface {
// GetStorageConfiguration will return the storage configuration to be used
// for this PVC role and this cluster
GetStorageConfiguration(cluster *apiv1.Cluster) (apiv1.StorageConfiguration, error)
// GetSource gets the PVC source to be used when creating a new PVC
GetSource(source *StorageSource) (*corev1.TypedLocalObjectReference, error)
}
// ExpectedObjectCalculator returns the data needed for a given pvc
type ExpectedObjectCalculator interface {
Bootstrap
Backup
Configuration
Meta
}
// GetExpectedObjectCalculator return an object capable of determining a series of data for the given pvc
func GetExpectedObjectCalculator(labels map[string]string) (ExpectedObjectCalculator, error) {
roleName := labels[utils.PvcRoleLabelName]
tbsName := labels[utils.TablespaceNameLabelName]
switch utils.PVCRole(roleName) {
case utils.PVCRolePgData:
return NewPgDataCalculator(), nil
case utils.PVCRolePgWal:
return NewPgWalCalculator(), nil
case utils.PVCRolePgTablespace:
return NewPgTablespaceCalculator(tbsName), nil
default:
return nil, fmt.Errorf("unknown pvc role name: %s", roleName)
}
}
// pgDataCalculator describes the role of a PVC which used for pg_data
type pgDataCalculator struct{}
// pgWalCalculator describes the role of a PVC which used for pg_wal
type pgWalCalculator struct{}
// pgTablespaceCalculator describes the role of a PVC which used for tablespace
type pgTablespaceCalculator struct {
tablespaceName string
}
// NewPgDataCalculator returns a ExpectedObjectCalculator for a PVC of PG_DATA type
func NewPgDataCalculator() ExpectedObjectCalculator {
return pgDataCalculator{}
}
// NewPgWalCalculator returns a ExpectedObjectCalculator for a PVC of PG_WAL type
func NewPgWalCalculator() ExpectedObjectCalculator {
return pgWalCalculator{}
}
func newTablespaceMetaCalculator() Meta {
return pgTablespaceCalculator{}
}
// NewPgTablespaceCalculator returns a ExpectedObjectCalculator for a PVC of PG_TABLESPACE type
func NewPgTablespaceCalculator(tbsName string) ExpectedObjectCalculator {
return pgTablespaceCalculator{tablespaceName: tbsName}
}
// GetLabels will be used as the label value
func (r pgDataCalculator) GetLabels(instanceName string) map[string]string {
labels := map[string]string{
utils.InstanceNameLabelName: instanceName,
utils.PvcRoleLabelName: string(utils.PVCRolePgData),
}
return labels
}
// GetName will be used to get the name of the PVC
func (r pgDataCalculator) GetName(instanceName string) string {
return instanceName
}
// GetStorageConfiguration will return the storage configuration to be used
// for this PVC role and this cluster
func (r pgDataCalculator) GetStorageConfiguration(cluster *apiv1.Cluster) (apiv1.StorageConfiguration, error) {
return cluster.Spec.StorageConfiguration, nil
}
// GetSource gets the PVC source to be used when creating a new PVC
func (r pgDataCalculator) GetSource(source *StorageSource) (*corev1.TypedLocalObjectReference, error) {
if source == nil {
return nil, nil
}
return &source.DataSource, nil
}
// GetRoleName return the role name in string
func (r pgDataCalculator) GetRoleName() string {
return string(utils.PVCRolePgData)
}
// GetInitialStatus returns the status the PVC should be first created with
func (r pgDataCalculator) GetInitialStatus() PVCStatus {
return StatusInitializing
}
// GetSnapshotName gets the snapshot name for a certain PVC
func (r pgDataCalculator) GetSnapshotName(backupName string) string {
return backupName
}
// GetVolumeSnapshotClass implements the Role interface
func (r pgDataCalculator) GetVolumeSnapshotClass(configuration *apiv1.VolumeSnapshotConfiguration) *string {
if len(configuration.ClassName) > 0 {
return ptr.To(configuration.ClassName)
}
return nil
}
// GetSourceFromBackup implements the Role interface
func (r pgDataCalculator) GetSourceFromBackup(backup *apiv1.Backup) *corev1.TypedLocalObjectReference {
for _, element := range backup.Status.BackupSnapshotStatus.Elements {
if element.Type == string(utils.PVCRolePgData) {
return &corev1.TypedLocalObjectReference{
APIGroup: ptr.To(volumesnapshot.GroupName),
Kind: apiv1.VolumeSnapshotKind,
Name: element.Name,
}
}
}
return nil
}
// GetLabels will be used as the label value
func (r pgWalCalculator) GetLabels(instanceName string) map[string]string {
labels := map[string]string{
utils.InstanceNameLabelName: instanceName,
utils.PvcRoleLabelName: string(utils.PVCRolePgWal),
}
return labels
}
// GetName will be used to get the name of the PVC
func (r pgWalCalculator) GetName(instanceName string) string {
return instanceName + apiv1.WalArchiveVolumeSuffix
}
// GetStorageConfiguration will return the storage configuration to be used
// for this PVC role and this cluster
func (r pgWalCalculator) GetStorageConfiguration(cluster *apiv1.Cluster) (apiv1.StorageConfiguration, error) {
return *cluster.Spec.WalStorage, nil
}
// GetSource gets the PVC source to be used when creating a new PVC
func (r pgWalCalculator) GetSource(source *StorageSource) (*corev1.TypedLocalObjectReference, error) {
if source == nil {
return nil, nil
}
if source.WALSource == nil {
return nil, fmt.Errorf("missing StorageSource for PostgreSQL WAL (Write-Ahead Log) PVC")
}
return source.WALSource, nil
}
// GetRoleName return the role name in string
func (r pgWalCalculator) GetRoleName() string {
return string(utils.PVCRolePgWal)
}
// GetInitialStatus returns the status the PVC should be first created with
func (r pgWalCalculator) GetInitialStatus() PVCStatus {
return StatusReady
}
// GetSnapshotName gets the snapshot name for a certain PVC
func (r pgWalCalculator) GetSnapshotName(backupName string) string {
return fmt.Sprintf("%s%s", backupName, apiv1.WalArchiveVolumeSuffix)
}
// GetVolumeSnapshotClass implements the Role interface
func (r pgWalCalculator) GetVolumeSnapshotClass(configuration *apiv1.VolumeSnapshotConfiguration) *string {
if len(configuration.WalClassName) > 0 {
return ptr.To(configuration.WalClassName)
}
if len(configuration.ClassName) > 0 {
return ptr.To(configuration.ClassName)
}
return nil
}
// GetLabels will be used as the label value
func (r pgTablespaceCalculator) GetLabels(instanceName string) map[string]string {
labels := map[string]string{
utils.InstanceNameLabelName: instanceName,
utils.PvcRoleLabelName: string(utils.PVCRolePgTablespace),
}
// we need empty check here as we don't want to impact the label filter with empty value
if r.tablespaceName != "" {
labels[utils.TablespaceNameLabelName] = r.tablespaceName
}
return labels
}
// GetName will be used to get the name of the PVC
func (r pgTablespaceCalculator) GetName(instanceName string) string {
pvcName := specs.PvcNameForTablespace(instanceName, r.tablespaceName)
return pvcName
}
// GetStorageConfiguration will return the storage configuration to be used
// for this PVC role and this cluster
func (r pgTablespaceCalculator) GetStorageConfiguration(cluster *apiv1.Cluster) (apiv1.StorageConfiguration, error) {
var storageConfiguration *apiv1.StorageConfiguration
for _, tbsConfig := range cluster.Spec.Tablespaces {
tbsConfig := tbsConfig
if tbsConfig.Name == r.tablespaceName {
storageConfiguration = &tbsConfig.Storage
break
}
}
if storageConfiguration == nil {
return apiv1.StorageConfiguration{},
fmt.Errorf(
"storage configuration doesn't exist for the given PVC role: %s and label %s",
utils.PVCRolePgTablespace,
r.tablespaceName,
)
}
return *storageConfiguration, nil
}
// GetSource gets the PVC source to be used when creating a new PVC
func (r pgTablespaceCalculator) GetSource(source *StorageSource) (*corev1.TypedLocalObjectReference, error) {
if source == nil {
return nil, nil
}
if s, has := source.TablespaceSource[r.tablespaceName]; has {
return &s, nil
}
return nil, fmt.Errorf("missing StorageSource for tablespace %s PVC", r.tablespaceName)
}
// GetRoleName return the role name in string
func (r pgTablespaceCalculator) GetRoleName() string {
return string(utils.PVCRolePgTablespace)
}
// GetInitialStatus returns the status the PVC should be first created with
func (r pgTablespaceCalculator) GetInitialStatus() PVCStatus {
return StatusReady
}
// GetSnapshotName gets the snapshot name for a certain PVC
func (r pgTablespaceCalculator) GetSnapshotName(backupName string) string {
return specs.SnapshotBackupNameForTablespace(backupName, r.tablespaceName)
}
// GetVolumeSnapshotClass implements the Role interface
func (r pgTablespaceCalculator) GetVolumeSnapshotClass(configuration *apiv1.VolumeSnapshotConfiguration) *string {
if className, ok := configuration.TablespaceClassName[r.tablespaceName]; ok && len(className) > 0 {
return ptr.To(className)
}
if len(configuration.ClassName) > 0 {
return ptr.To(configuration.ClassName)
}
return nil
}