Skip to content

Commit 0da3832

Browse files
andrewlecuyerJeff McCormick
authored andcommitted
Implement Node Label Functionality for pgdump Restores (#585)
* Implemented node label functionality for pgdump restores. Also, refactored node label validation, and corrected a defect in which and node label with an empty value was considered valid. * Make pgbasebackup the default for the "pgo backup" command
1 parent 45e8f8b commit 0da3832

File tree

12 files changed

+98
-95
lines changed

12 files changed

+98
-95
lines changed

apiserver/backrestservice/backrestimpl.go

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -445,24 +445,14 @@ func getRestoreParams(request *msgs.RestoreRequest, ns string, cluster crv1.Pgcl
445445
spec.Parameters[util.LABEL_PGBACKREST_REPO_PATH] = "/backrestrepo/" + request.FromCluster + "-backrest-shared-repo"
446446
spec.Parameters[util.LABEL_PGBACKREST_REPO_HOST] = request.FromCluster + "-backrest-shared-repo"
447447

448+
// validate & parse nodeLabel if exists
448449
if request.NodeLabel != "" {
449-
parts := strings.Split(request.NodeLabel, "=")
450-
if len(parts) != 2 {
451-
return nil, errors.New(request.NodeLabel + " node label does not follow key=value format")
452-
}
453450

454-
keyValid, valueValid, err := apiserver.IsValidNodeLabel(parts[0], parts[1])
455-
if err != nil {
451+
if err := apiserver.ValidateNodeLabel(request.NodeLabel); err != nil {
456452
return nil, err
457453
}
458454

459-
if !keyValid {
460-
return nil, errors.New(request.NodeLabel + " key was not valid .. check node labels for correct values to specify")
461-
}
462-
if !valueValid {
463-
return nil, errors.New(request.NodeLabel + " node label value was not valid .. check node labels for correct values to specify")
464-
}
465-
455+
parts := strings.Split(request.NodeLabel, "=")
466456
spec.Parameters[util.LABEL_NODE_LABEL_KEY] = parts[0]
467457
spec.Parameters[util.LABEL_NODE_LABEL_VALUE] = parts[1]
468458

apiserver/clusterservice/clusterimpl.go

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ import (
2020
"errors"
2121
"fmt"
2222

23-
log "github.com/Sirupsen/logrus"
24-
crv1 "github.com/crunchydata/postgres-operator/apis/cr/v1"
25-
"github.com/crunchydata/postgres-operator/apiserver"
2623
"io/ioutil"
2724
"strconv"
2825
"strings"
2926
"time"
3027

28+
log "github.com/Sirupsen/logrus"
29+
crv1 "github.com/crunchydata/postgres-operator/apis/cr/v1"
30+
"github.com/crunchydata/postgres-operator/apiserver"
31+
3132
msgs "github.com/crunchydata/postgres-operator/apiservermsgs"
3233
"github.com/crunchydata/postgres-operator/config"
3334
"github.com/crunchydata/postgres-operator/kubeapi"
@@ -652,34 +653,20 @@ func CreateCluster(request *msgs.CreateClusterRequest, ns string) msgs.CreateClu
652653
log.Debug("primary node labels used from pgo.yaml")
653654
}
654655

656+
// validate & parse nodeLabel if exists
655657
if request.NodeLabel != "" {
656-
parts := strings.Split(request.NodeLabel, "=")
657-
if len(parts) != 2 {
658-
resp.Status.Code = msgs.Error
659-
resp.Status.Msg = request.NodeLabel + " node label does not follow key=value format"
660-
return resp
661-
}
662658

663-
keyValid, valueValid, err := apiserver.IsValidNodeLabel(parts[0], parts[1])
664-
if err != nil {
659+
if err = apiserver.ValidateNodeLabel(request.NodeLabel); err != nil {
665660
resp.Status.Code = msgs.Error
666661
resp.Status.Msg = err.Error()
667662
return resp
668663
}
669664

670-
if !keyValid {
671-
resp.Status.Code = msgs.Error
672-
resp.Status.Msg = request.NodeLabel + " key was not valid .. check node labels for correct values to specify"
673-
return resp
674-
}
675-
if !valueValid {
676-
resp.Status.Code = msgs.Error
677-
resp.Status.Msg = request.NodeLabel + " node label value was not valid .. check node labels for correct values to specify"
678-
return resp
679-
}
680-
log.Debug("primary node labels used from user entered flag")
665+
parts := strings.Split(request.NodeLabel, "=")
681666
userLabelsMap[util.LABEL_NODE_LABEL_KEY] = parts[0]
682667
userLabelsMap[util.LABEL_NODE_LABEL_VALUE] = parts[1]
668+
669+
log.Debug("primary node labels used from user entered flag")
683670
}
684671

685672
if request.ReplicaStorageConfig != "" {

apiserver/clusterservice/scaleimpl.go

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ limitations under the License.
1717

1818
import (
1919
"fmt"
20+
"strconv"
21+
"strings"
22+
2023
log "github.com/Sirupsen/logrus"
2124
crv1 "github.com/crunchydata/postgres-operator/apis/cr/v1"
2225
"github.com/crunchydata/postgres-operator/apiserver"
@@ -28,8 +31,6 @@ import (
2831
"k8s.io/api/extensions/v1beta1"
2932
kerrors "k8s.io/apimachinery/pkg/api/errors"
3033
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31-
"strconv"
32-
"strings"
3334
)
3435

3536
// ScaleCluster ...
@@ -117,36 +118,20 @@ func ScaleCluster(name, replicaCount, resourcesConfig, storageConfig, nodeLabel,
117118
log.Debug("using pgo.yaml ReplicaNodeLabel for replica creation")
118119
}
119120

120-
//validate nodeLabel
121+
// validate & parse nodeLabel if exists
121122
if nodeLabel != "" {
122-
parts = strings.Split(nodeLabel, "=")
123-
if len(parts) != 2 {
124-
response.Status.Code = msgs.Error
125-
response.Status.Msg = nodeLabel + " node label does not follow key=value format"
126-
return response
127-
}
128123

129-
keyValid, valueValid, err := apiserver.IsValidNodeLabel(parts[0], parts[1])
130-
if err != nil {
124+
if err = apiserver.ValidateNodeLabel(nodeLabel); err != nil {
131125
response.Status.Code = msgs.Error
132126
response.Status.Msg = err.Error()
133127
return response
134128
}
135129

136-
if !keyValid {
137-
response.Status.Code = msgs.Error
138-
response.Status.Msg = nodeLabel + " key was not valid .. check node labels for correct values to specify"
139-
return response
140-
}
141-
if !valueValid {
142-
response.Status.Code = msgs.Error
143-
response.Status.Msg = nodeLabel + " node label value was not valid .. check node labels for correct values to specify"
144-
return response
145-
}
130+
parts := strings.Split(nodeLabel, "=")
146131
spec.UserLabels[util.LABEL_NODE_LABEL_KEY] = parts[0]
147132
spec.UserLabels[util.LABEL_NODE_LABEL_VALUE] = parts[1]
148-
log.Debug("using user entered node label for replica creation")
149133

134+
log.Debug("using user entered node label for replica creation")
150135
}
151136

152137
labels := make(map[string]string)

apiserver/pgdumpservice/pgdumpimpl.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ limitations under the License.
1818
import (
1919
"errors"
2020
"fmt"
21+
"strconv"
22+
"strings"
23+
2124
log "github.com/Sirupsen/logrus"
2225
crv1 "github.com/crunchydata/postgres-operator/apis/cr/v1"
2326
"github.com/crunchydata/postgres-operator/apiserver"
@@ -27,8 +30,6 @@ import (
2730
"k8s.io/api/core/v1"
2831
kerrors "k8s.io/apimachinery/pkg/api/errors"
2932
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30-
"strconv"
31-
"strings"
3233
)
3334

3435
const pgDumpCommand = "pgdump"
@@ -446,7 +447,13 @@ func Restore(request *msgs.PgRestoreRequest, ns string) msgs.PgRestoreResponse {
446447

447448
// pgtask := getRestoreParams(cluster)
448449

449-
pgtask := buildPgTaskForRestore(taskName, crv1.PgtaskpgRestore, request)
450+
pgtask, err := buildPgTaskForRestore(taskName, crv1.PgtaskpgRestore, request)
451+
if err != nil {
452+
resp.Status.Code = msgs.Error
453+
resp.Status.Msg = err.Error()
454+
return resp
455+
}
456+
450457
existingTask := crv1.Pgtask{}
451458

452459
//delete any existing pgtask with the same name
@@ -482,7 +489,7 @@ func Restore(request *msgs.PgRestoreRequest, ns string) msgs.PgRestoreResponse {
482489
}
483490

484491
// builds out a pgTask structure that can be handed to kube
485-
func buildPgTaskForRestore(taskName string, action string, request *msgs.PgRestoreRequest) *crv1.Pgtask {
492+
func buildPgTaskForRestore(taskName string, action string, request *msgs.PgRestoreRequest) (*crv1.Pgtask, error) {
486493

487494
var newInstance *crv1.Pgtask
488495
var storageSpec crv1.PgStorageSpec
@@ -508,6 +515,21 @@ func buildPgTaskForRestore(taskName string, action string, request *msgs.PgResto
508515

509516
spec.Parameters[util.LABEL_PGRESTORE_PORT] = apiserver.Pgo.Cluster.Port
510517
spec.Parameters[util.LABEL_CCP_IMAGE_TAG_KEY] = apiserver.Pgo.Cluster.CCPImageTag
518+
519+
// validate & parse nodeLabel if exists
520+
if request.NodeLabel != "" {
521+
522+
if err := apiserver.ValidateNodeLabel(request.NodeLabel); err != nil {
523+
return nil, err
524+
}
525+
526+
parts := strings.Split(request.NodeLabel, "=")
527+
spec.Parameters[util.LABEL_NODE_LABEL_KEY] = parts[0]
528+
spec.Parameters[util.LABEL_NODE_LABEL_VALUE] = parts[1]
529+
530+
log.Debug("Restore node labels used from user entered flag")
531+
}
532+
511533
spec.StorageSpec = storageSpec
512534

513535
newInstance = &crv1.Pgtask{
@@ -516,7 +538,7 @@ func buildPgTaskForRestore(taskName string, action string, request *msgs.PgResto
516538
},
517539
Spec: spec,
518540
}
519-
return newInstance
541+
return newInstance, nil
520542
}
521543

522544
// TODO: Needed?

apiserver/root.go

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,29 @@ func IsValidStorageName(name string) bool {
415415
return ok
416416
}
417417

418+
// ValidateNodeLabel
419+
// returns error if node label is invalid
420+
func ValidateNodeLabel(nodeLabel string) error {
421+
parts := strings.Split(nodeLabel, "=")
422+
if len(parts) != 2 {
423+
return errors.New(nodeLabel + " node label does not follow key=value format")
424+
}
425+
426+
keyValid, valueValid, err := IsValidNodeLabel(parts[0], parts[1])
427+
if err != nil {
428+
return err
429+
}
430+
431+
if !keyValid {
432+
return errors.New(nodeLabel + " key was not valid .. check node labels for correct values to specify")
433+
}
434+
if !valueValid {
435+
return errors.New(nodeLabel + " node label value was not valid .. check node labels for correct values to specify")
436+
}
437+
438+
return nil
439+
}
440+
418441
// IsValidNodeLabel
419442
// returns bool for key validity
420443
// returns bool for value validity
@@ -430,14 +453,13 @@ func IsValidNodeLabel(key, value string) (bool, bool, error) {
430453
return false, false, err
431454
}
432455

433-
var v string
434456
for _, node := range nodes.Items {
435-
v = node.ObjectMeta.Labels[key]
436-
if v != "" {
457+
458+
if val, exists := node.ObjectMeta.Labels[key]; exists {
437459
keyValid = true
438-
}
439-
if v == value {
440-
valueValid = true
460+
if val == value {
461+
valueValid = true
462+
}
441463
}
442464
}
443465

@@ -499,27 +521,15 @@ func validateWithKube() {
499521

500522
for _, n := range configNodeLabels {
501523

524+
//parse & validate pgo.yaml node labels if set
502525
if n != "" {
503-
parts := strings.Split(n, "=")
504-
if len(parts) != 2 {
505-
log.Error(n + " node label in pgo.yaml does not follow key=value format")
506-
os.Exit(2)
507-
}
508526

509-
keyValid, valueValid, err := IsValidNodeLabel(parts[0], parts[1])
510-
if err != nil {
527+
if err := ValidateNodeLabel(n); err != nil {
528+
log.Error(n + " node label specified in pgo.yaml is invalid")
511529
log.Error(err)
512530
os.Exit(2)
513531
}
514532

515-
if !keyValid {
516-
log.Error(n + " key not a valid node label key in pgo.yaml")
517-
os.Exit(2)
518-
}
519-
if !valueValid {
520-
log.Error(n + "value not a valid node label value in pgo.yaml ")
521-
os.Exit(2)
522-
}
523533
log.Debugf("%s is a valid pgo.yaml node label default", n)
524534
}
525535
}

apiservermsgs/pgdumpmsgs.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ See the License for the specific language governing permissions and
1515
limitations under the License.
1616
*/
1717

18-
import (
1918
//crv1 "github.com/crunchydata/postgres-operator/apis/cr/v1"
20-
)
2119

2220
type CreatepgDumpBackupResponse struct {
2321
Results []string
@@ -49,4 +47,5 @@ type PgRestoreRequest struct {
4947
FromPVC string
5048
RestoreOpts string
5149
PITRTarget string
50+
NodeLabel string
5251
}

conf/postgres-operator/pgrestore-job.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@
8686
]
8787
}
8888
],
89-
"restartPolicy": "Never"
89+
{{.NodeSelector}}
90+
"restartPolicy": "Never"
9091
}
9192
}
9293
}

hugo/content/Design/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ on PG 9.6/9.5 systems, the command you will use is *select pg_xlog_replay_resume
176176
* there is currently no Operator validation of user entered pgBackRest command options, you will need to make sure to enter these correctly, if not the pgBackRest restore command can fail.
177177
* the restore workflow does not perform a backup after the restore nor does it verify that any replicas are in a working status after the restore, it is possible you might have to take actions on the replica to get them back to replicating with the new restored primary.
178178
* pgbackrest.org suggests running a pgbackrest backup after a restore, this needs to be done by the DBA as part of a restore
179-
* when performing a restore, the **node-label** option can be utilized to target a specific node for both the pgBackRest restore job and the new (i.e. restored) primary deployment that is then created for the cluster. If a node label is not specified, the restore job will not target any specific node, and the restored primary deployment will inherit any node label's defined for the original primary deployment.
179+
* when performing a pgBackRest restore, the **node-label** flag can be utilized to target a specific node for both the pgBackRest restore job and the new (i.e. restored) primary deployment that is then created for the cluster. If a node label is not specified, the restore job will not target any specific node, and the restored primary deployment will inherit any node label's defined for the original primary deployment.
180180

181181
## PGO Scheduler
182182

operator/pgdump/restore.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package pgdump
1818
import (
1919
"bytes"
2020
"encoding/json"
21+
"os"
22+
2123
log "github.com/Sirupsen/logrus"
2224
crv1 "github.com/crunchydata/postgres-operator/apis/cr/v1"
2325
"github.com/crunchydata/postgres-operator/kubeapi"
@@ -27,7 +29,6 @@ import (
2729
v1batch "k8s.io/api/batch/v1"
2830
"k8s.io/client-go/kubernetes"
2931
"k8s.io/client-go/rest"
30-
"os"
3132
)
3233

3334
type restorejobTemplateFields struct {
@@ -45,6 +46,7 @@ type restorejobTemplateFields struct {
4546
CCPImagePrefix string
4647
CCPImageTag string
4748
PgPort string
49+
NodeSelector string
4850
}
4951

5052
// Restore ...
@@ -90,6 +92,7 @@ func Restore(namespace string, clientset *kubernetes.Clientset, restclient *rest
9092
PITRTarget: task.Spec.Parameters[util.LABEL_PGRESTORE_PITR_TARGET],
9193
CCPImagePrefix: operator.Pgo.Cluster.CCPImagePrefix,
9294
CCPImageTag: operator.Pgo.Cluster.CCPImageTag,
95+
NodeSelector: operator.GetAffinity(task.Spec.Parameters["NodeLabelKey"], task.Spec.Parameters["NodeLabelValue"], "In"),
9396
}
9497

9598
var doc2 bytes.Buffer

pgo/cmd/backup.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ var backupCmd = &cobra.Command{
6060

6161
createBackrestBackup(args, Namespace)
6262

63-
case labelutil.LABEL_BACKUP_TYPE_BASEBACKUP:
63+
case "", labelutil.LABEL_BACKUP_TYPE_BASEBACKUP:
6464

6565
createBackup(args, Namespace)
6666

0 commit comments

Comments
 (0)