-
Notifications
You must be signed in to change notification settings - Fork 12
/
start.go
165 lines (145 loc) · 5.07 KB
/
start.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
// Copyright 2021 - 2024 Crunchy Data Solutions, Inc.
//
// 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 cmd
import (
"context"
"fmt"
"github.com/spf13/cobra"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/dynamic"
"github.com/crunchydata/postgres-operator-client/internal"
"github.com/crunchydata/postgres-operator-client/internal/apis/postgres-operator.crunchydata.com/v1beta1"
)
type ShutdownRequestArgs struct {
ClusterName string
Config *internal.Config
ForceConflicts bool
Namespace string
NewShutdownValue bool
Mapping *meta.RESTMapping
}
func newStartCommand(config *internal.Config) *cobra.Command {
cmdStart := &cobra.Command{
Use: "start CLUSTER_NAME",
Short: "Start cluster",
Long: `Start sets the spec.shutdown field to false, allowing you to start a PostgreSQL cluster.
The --force-conflicts flag may be required if the spec.shutdown field has been updated by another client.
### RBAC Requirements
Resources Verbs
--------- -----
postgresclusters.postgres-operator.crunchydata.com [get patch]
### Usage`,
}
cmdStart.Example = internal.FormatExample(`# Start a 'hippo' postgrescluster.
pgo start hippo
# Resolve ownership conflict
pgo start hippo --force-conflicts
### Example output
postgresclusters/hippo start initiated`)
// Limit the number of args, that is, only one cluster name
cmdStart.Args = cobra.ExactArgs(1)
var forceConflicts bool
cmdStart.Flags().BoolVar(&forceConflicts, "force-conflicts", false, "take ownership and overwrite the shutdown setting")
cmdStart.RunE = func(cmd *cobra.Command, args []string) error {
mapping, client, err := v1beta1.NewPostgresClusterClient(config)
if err != nil {
return err
}
namespace, err := config.Namespace()
if err != nil {
return err
}
requestArgs := ShutdownRequestArgs{
ClusterName: args[0],
Config: config,
ForceConflicts: forceConflicts,
Namespace: namespace,
NewShutdownValue: false,
Mapping: mapping,
}
cluster, err := getPostgresCluster(client, requestArgs)
if err != nil {
return err
}
msg, err := patchClusterShutdown(cluster, client, requestArgs)
if msg != "" {
cmd.Print(msg)
}
if err != nil {
return err
}
return nil
}
return cmdStart
}
func patchClusterShutdown(cluster *unstructured.Unstructured, client dynamic.NamespaceableResourceInterface, args ShutdownRequestArgs) (string, error) {
ctx := context.Background()
currShutdownVal, found, err := unstructured.NestedBool(cluster.Object, "spec", "shutdown")
if err != nil {
return "", err
}
// If the shutdown status is equal to the intent of the command, do nothing.
if found && currShutdownVal == args.NewShutdownValue {
// If NewShutdownValue == true, we intend to stop the cluster.
if args.NewShutdownValue {
return "Cluster already Stopped. Nothing to do.\n", nil
}
return "Cluster already Started. Nothing to do.\n", nil
}
// Construct the payload.
intent := new(unstructured.Unstructured)
if err := internal.ExtractFieldsInto(cluster, intent, args.Config.Patch.FieldManager); err != nil {
return "", err
}
if err := unstructured.SetNestedField(intent.Object, args.NewShutdownValue, "spec", "shutdown"); err != nil {
return "", err
}
patch, err := intent.MarshalJSON()
if err != nil {
return "", err
}
patchOptions := metav1.PatchOptions{}
if args.ForceConflicts {
b := true
patchOptions.Force = &b
}
// Patch the update.
_, err = client.Namespace(args.Namespace).Patch(ctx,
args.ClusterName, types.ApplyPatchType, patch,
args.Config.Patch.PatchOptions(patchOptions))
if err != nil {
if apierrors.IsConflict(err) {
return "SUGGESTION: The --force-conflicts flag may help in performing this operation.\n", err
}
return "", err
}
var initiatedMsg string
// If NewShutdownValue == true, we intend to stop the cluster.
if args.NewShutdownValue {
initiatedMsg = "stop initiated"
} else {
initiatedMsg = "start initiated"
}
return fmt.Sprintf("%s/%s %s\n", args.Mapping.Resource.Resource, args.ClusterName, initiatedMsg), err
}
func getPostgresCluster(client dynamic.NamespaceableResourceInterface, args ShutdownRequestArgs) (*unstructured.Unstructured, error) {
ctx := context.Background()
cluster, err := client.Namespace(args.Namespace).Get(ctx,
args.ClusterName, metav1.GetOptions{})
return cluster, err
}