@@ -43,6 +43,8 @@ import (
43
43
44
44
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
45
45
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
46
+ "github.com/arangodb/kube-arangodb/pkg/util"
47
+ "github.com/arangodb/kube-arangodb/pkg/util/cli"
46
48
"github.com/arangodb/kube-arangodb/pkg/util/constants"
47
49
"github.com/arangodb/kube-arangodb/pkg/util/errors"
48
50
"github.com/arangodb/kube-arangodb/pkg/util/globals"
@@ -51,76 +53,149 @@ import (
51
53
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
52
54
)
53
55
54
- const ArgDeploymentName = "deployment-name"
56
+ const (
57
+ ArgDeploymentName = "deployment-name"
58
+ ArgMemberName = "member-name"
59
+ ArgAcceptedCode = "accepted-code"
60
+ )
55
61
56
62
func init () {
57
- var deploymentName string
58
-
59
63
cmdMain .AddCommand (cmdAdmin )
60
- cmdAdmin .AddCommand (cmdAgency )
64
+ cmdAdmin .AddCommand (cmdAdminAgency )
65
+ cmdAdmin .AddCommand (cmdAdminMember )
61
66
62
- cmdAgency .AddCommand (cmdAgencyDump )
63
- cmdAgencyDump .Flags ().StringVarP ( & deploymentName , ArgDeploymentName , "d" , "" ,
67
+ cmdAdminAgency .AddCommand (cmdAdminAgencyDump )
68
+ cmdAdminAgencyDump .Flags ().StringP ( ArgDeploymentName , "d" , "" ,
64
69
"necessary when more than one deployment exist within on namespace" )
65
70
66
- cmdAgency .AddCommand (cmdAgencyState )
67
- cmdAgencyState .Flags ().StringVarP ( & deploymentName , ArgDeploymentName , "d" , "" ,
71
+ cmdAdminAgency .AddCommand (cmdAdminAgencyState )
72
+ cmdAdminAgencyState .Flags ().StringP ( ArgDeploymentName , "d" , "" ,
68
73
"necessary when more than one deployment exist within on namespace" )
74
+
75
+ cmdAdminMember .AddCommand (cmdAdminMemberRequest )
76
+ cmdAdminMemberRequest .AddCommand (cmdAdminMemberRequestGet )
77
+ cmdAdminMemberRequestGet .Flags ().StringP (ArgDeploymentName , "d" , "" ,
78
+ "necessary when more than one deployment exist within on namespace" )
79
+ cmdAdminMemberRequestGet .Flags ().StringP (ArgMemberName , "m" , "" ,
80
+ "name of the member for the dump" )
81
+ cmdAdminMemberRequestGet .Flags ().IntP (ArgAcceptedCode , "c" , 200 ,
82
+ "accepted command code" )
69
83
}
70
84
71
85
var cmdAdmin = & cobra.Command {
72
86
Use : "admin" ,
73
87
Short : "Administration operations" ,
74
- Run : adminShowUsage ,
88
+ RunE : cli . Usage ,
75
89
}
76
90
77
- var cmdAgency = & cobra.Command {
91
+ var cmdAdminMember = & cobra.Command {
92
+ Use : "member" ,
93
+ Short : "Member operations" ,
94
+ RunE : cli .Usage ,
95
+ }
96
+
97
+ var cmdAdminMemberRequest = & cobra.Command {
98
+ Use : "request" ,
99
+ Short : "Runs http request over member and returns object" ,
100
+ RunE : cli .Usage ,
101
+ }
102
+
103
+ var cmdAdminMemberRequestGet = & cobra.Command {
104
+ Use : "get" ,
105
+ Short : "GET Request" ,
106
+ RunE : cmdGetAdminMemberRequestGetE ,
107
+ }
108
+
109
+ var cmdAdminAgency = & cobra.Command {
78
110
Use : "agency" ,
79
111
Short : "Agency operations" ,
80
- Run : agencyShowUsage ,
112
+ RunE : cli . Usage ,
81
113
}
82
114
83
- var cmdAgencyDump = & cobra.Command {
115
+ var cmdAdminAgencyDump = & cobra.Command {
84
116
Use : "dump" ,
85
117
Short : "Get agency dump" ,
86
- Long : "It prints the agency history on the stdout" ,
87
- Run : cmdGetAgencyDump ,
118
+ Long : "Prints the agency history on the stdout" ,
119
+ RunE : cmdAdminGetAgencyDumpE ,
88
120
}
89
121
90
- var cmdAgencyState = & cobra.Command {
122
+ var cmdAdminAgencyState = & cobra.Command {
91
123
Use : "state" ,
92
124
Short : "Get agency state" ,
93
- Long : "It prints the agency current state on the stdout" ,
94
- Run : cmdGetAgencyState ,
125
+ Long : "Prints the agency current state on the stdout" ,
126
+ RunE : cmdAdminGetAgencyStateE ,
95
127
}
96
128
97
- func agencyShowUsage (cmd * cobra.Command , _ []string ) {
98
- cmd .Usage ()
99
- }
129
+ func cmdGetAdminMemberRequestGetE (cmd * cobra.Command , args []string ) error {
130
+ deploymentName , err := cmd .Flags ().GetString (ArgDeploymentName )
131
+ if err != nil {
132
+ return err
133
+ }
134
+ memberName , err := cmd .Flags ().GetString (ArgMemberName )
135
+ if err != nil {
136
+ return err
137
+ }
138
+ acceptedCode , err := cmd .Flags ().GetInt (ArgAcceptedCode )
139
+ if err != nil {
140
+ return err
141
+ }
142
+ ctx := getInterruptionContext ()
143
+ d , certCA , auth , err := getDeploymentAndCredentials (ctx , deploymentName )
144
+ if err != nil {
145
+ logger .Err (err ).Error ("failed to create basic data for the connection" )
146
+ return err
147
+ }
148
+
149
+ m , g , ok := d .Status .Members .ElementByID (memberName )
150
+ if ! ok {
151
+ err := errors .Errorf ("Unable to find member with id %s" , memberName )
152
+ logger .Err (err ).Error ("Unable to find member" )
153
+ return err
154
+ }
155
+
156
+ dnsName := k8sutil .CreatePodDNSName (d .GetObjectMeta (), g .AsRole (), m .ID )
157
+ endpoint := getArangoEndpoint (d .GetAcceptedSpec ().IsSecure (), dnsName )
158
+ conn := createClient ([]string {endpoint }, certCA , auth , connection .ApplicationJSON )
159
+ body , err := sendStreamRequest (ctx , conn , goHttp .MethodGet , nil , acceptedCode , args ... )
160
+ if body != nil {
161
+ defer body .Close ()
162
+ }
163
+ if err != nil {
164
+ logger .Err (err ).Error ("can not get dump" )
165
+ return err
166
+ }
100
167
101
- func adminShowUsage (cmd * cobra.Command , _ []string ) {
102
- cmd .Usage ()
168
+ // Print and receive parallely.
169
+ _ , err = io .Copy (os .Stdout , body )
170
+ return err
103
171
}
104
172
105
- func cmdGetAgencyState (cmd * cobra.Command , _ []string ) {
106
- deploymentName , _ := cmd .Flags ().GetString (ArgDeploymentName )
173
+ func cmdAdminGetAgencyStateE (cmd * cobra.Command , _ []string ) error {
174
+ deploymentName , err := cmd .Flags ().GetString (ArgDeploymentName )
175
+ if err != nil {
176
+ return err
177
+ }
107
178
ctx := getInterruptionContext ()
108
179
d , certCA , auth , err := getDeploymentAndCredentials (ctx , deploymentName )
109
180
if err != nil {
110
- logger .Err (err ).Fatal ("failed to create basic data for the connection" )
181
+ logger .Err (err ).Error ("failed to create basic data for the connection" )
182
+ return err
111
183
}
112
184
113
185
if d .GetAcceptedSpec ().GetMode () != api .DeploymentModeCluster {
114
- logger . Fatal ("agency state does not work for the \" %s\" deployment \" %s\" " , d .GetAcceptedSpec ().GetMode (),
186
+ err = errors . Errorf ("agency state does not work for the \" %s\" deployment \" %s\" " , d .GetAcceptedSpec ().GetMode (),
115
187
d .GetName ())
188
+ logger .Err (err ).Error ("Invalid deployment type" )
189
+ return err
116
190
}
117
191
118
192
dnsName := k8sutil .CreatePodDNSName (d .GetObjectMeta (), api .ServerGroupAgents .AsRole (), d .Status .Members .Agents [0 ].ID )
119
193
endpoint := getArangoEndpoint (d .GetAcceptedSpec ().IsSecure (), dnsName )
120
194
conn := createClient ([]string {endpoint }, certCA , auth , connection .ApplicationJSON )
121
195
leaderID , err := getAgencyLeader (ctx , conn )
122
196
if err != nil {
123
- logger .Err (err ).Fatal ("failed to get leader ID" )
197
+ logger .Err (err ).Error ("failed to get leader ID" )
198
+ return err
124
199
}
125
200
126
201
dnsLeaderName := k8sutil .CreatePodDNSName (d .GetObjectMeta (), api .ServerGroupAgents .AsRole (), leaderID )
@@ -131,24 +206,32 @@ func cmdGetAgencyState(cmd *cobra.Command, _ []string) {
131
206
defer body .Close ()
132
207
}
133
208
if err != nil {
134
- logger .Err (err ).Fatal ("can not get state of the agency" )
209
+ logger .Err (err ).Error ("can not get state of the agency" )
210
+ return err
135
211
}
136
212
137
- // Print and receive parallelly.
138
- io .Copy (os .Stdout , body )
213
+ // Print and receive parallely.
214
+ _ , err = io .Copy (os .Stdout , body )
215
+ return err
139
216
}
140
217
141
- func cmdGetAgencyDump (cmd * cobra.Command , _ []string ) {
142
- deploymentName , _ := cmd .Flags ().GetString (ArgDeploymentName )
218
+ func cmdAdminGetAgencyDumpE (cmd * cobra.Command , _ []string ) error {
219
+ deploymentName , err := cmd .Flags ().GetString (ArgDeploymentName )
220
+ if err != nil {
221
+ return err
222
+ }
143
223
ctx := getInterruptionContext ()
144
224
d , certCA , auth , err := getDeploymentAndCredentials (ctx , deploymentName )
145
225
if err != nil {
146
- logger .Err (err ).Fatal ("failed to create basic data for the connection" )
226
+ logger .Err (err ).Error ("failed to create basic data for the connection" )
227
+ return err
147
228
}
148
229
149
230
if d .GetAcceptedSpec ().GetMode () != api .DeploymentModeCluster {
150
- logger . Fatal ("agency dump does not work for the \" %s\" deployment \" %s\" " , d .GetAcceptedSpec ().GetMode (),
231
+ err = errors . Errorf ("agency state does not work for the \" %s\" deployment \" %s\" " , d .GetAcceptedSpec ().GetMode (),
151
232
d .GetName ())
233
+ logger .Err (err ).Error ("Invalid deployment type" )
234
+ return err
152
235
}
153
236
154
237
endpoint := getArangoEndpoint (d .GetAcceptedSpec ().IsSecure (), k8sutil .CreateDatabaseClientServiceDNSName (d .GetObjectMeta ()))
@@ -158,26 +241,62 @@ func cmdGetAgencyDump(cmd *cobra.Command, _ []string) {
158
241
defer body .Close ()
159
242
}
160
243
if err != nil {
161
- logger .Err (err ).Fatal ("can not get dump" )
244
+ logger .Err (err ).Error ("can not get dump" )
245
+ return err
162
246
}
163
247
164
- // Print and receive parallelly.
165
- io .Copy (os .Stdout , body )
248
+ // Print and receive parallely.
249
+ _ , err = io .Copy (os .Stdout , body )
250
+ return err
166
251
}
167
252
168
- // getAgencyState returns the current state in the agency.
169
- func getAgencyState (ctx context.Context , conn connection.Connection ) (io.ReadCloser , error ) {
170
- url := connection .NewUrl ("_api" , "agency" , "read" )
171
- data := []byte (`[["/"]]` )
172
- resp , body , err := connection .CallStream (ctx , conn , goHttp .MethodPost , url , connection .WithBody (data ))
253
+ // sendStreamRequest sends the request to a member
254
+ func sendStreamRequest (ctx context.Context , conn connection.Connection , method string , body []byte , code int , parts ... string ) (io.ReadCloser , error ) {
255
+ url := connection .NewUrl (parts ... )
256
+
257
+ var mods []connection.RequestModifier
258
+
259
+ if body != nil {
260
+ mods = append (mods , connection .WithBody (body ))
261
+ }
262
+
263
+ resp , output , err := connection .CallStream (ctx , conn , method , url , mods ... )
173
264
if err != nil {
174
265
return nil , err
175
266
}
176
- if resp .Code () != goHttp .StatusOK {
177
- return nil , errors .New (fmt .Sprintf ("unexpected HTTP status from \" %s\" endpoint" , url ))
267
+ if resp .Code () != code {
268
+ return nil , errors .New (fmt .Sprintf ("unexpected HTTP status from \" %s\" endpoint. Expected: '%d', got '%d'" , url , code , resp .Code ()))
269
+ }
270
+
271
+ return output , nil
272
+ }
273
+
274
+ // sendRequest sends the request to a member and returns object
275
+ func sendRequest [OUT any ](ctx context.Context , conn connection.Connection , method string , body []byte , code int , parts ... string ) (OUT , error ) {
276
+ url := connection .NewUrl (parts ... )
277
+
278
+ var mods []connection.RequestModifier
279
+
280
+ if body != nil {
281
+ mods = append (mods , connection .WithBody (body ))
282
+ }
283
+
284
+ var out OUT
285
+
286
+ resp , err := connection .Call (ctx , conn , method , url , & out , mods ... )
287
+ if err != nil {
288
+ return util .Default [OUT ](), err
289
+ }
290
+ if resp .Code () != code {
291
+ return util .Default [OUT ](), errors .New (fmt .Sprintf ("unexpected HTTP status from \" %s\" endpoint. Expected: '%d', got '%d'" , url , code , resp .Code ()))
178
292
}
179
293
180
- return body , nil
294
+ return out , nil
295
+ }
296
+
297
+ // getAgencyState returns the current state in the agency.
298
+ func getAgencyState (ctx context.Context , conn connection.Connection ) (io.ReadCloser , error ) {
299
+ return sendStreamRequest (ctx , conn , goHttp .MethodPost , []byte (`[["/"]]` ), goHttp .StatusOK , "_api" , "agency" , "read" )
181
300
}
182
301
183
302
// getDeploymentAndCredentials returns deployment and necessary credentials to communicate with ArangoDB pods.
@@ -235,15 +354,10 @@ func getArangoEndpoint(secure bool, dnsName string) string {
235
354
236
355
// getAgencyLeader returns the leader ID of the agency.
237
356
func getAgencyLeader (ctx context.Context , conn connection.Connection ) (string , error ) {
238
- url := connection .NewUrl ("_api" , "agency" , "config" )
239
- output := make (map [string ]interface {})
240
- resp , err := connection .CallGet (ctx , conn , url , & output )
357
+ output , err := sendRequest [map [string ]interface {}](ctx , conn , goHttp .MethodGet , nil , goHttp .StatusOK , "_api" , "agency" , "config" )
241
358
if err != nil {
242
359
return "" , err
243
360
}
244
- if resp .Code () != goHttp .StatusOK {
245
- return "" , errors .New ("unexpected HTTP status from agency-dump endpoint" )
246
- }
247
361
248
362
if leaderID , ok := output ["leaderId" ]; ok {
249
363
if id , ok := leaderID .(string ); ok {
@@ -256,16 +370,7 @@ func getAgencyLeader(ctx context.Context, conn connection.Connection) (string, e
256
370
257
371
// getAgencyDump returns dump of the agency.
258
372
func getAgencyDump (ctx context.Context , conn connection.Connection ) (io.ReadCloser , error ) {
259
- url := connection .NewUrl ("_api" , "cluster" , "agency-dump" )
260
- resp , body , err := connection .CallStream (ctx , conn , goHttp .MethodGet , url )
261
- if err != nil {
262
- return nil , err
263
- }
264
- if resp .Code () != goHttp .StatusOK {
265
- return nil , errors .New ("unexpected HTTP status from agency-dump endpoint" )
266
- }
267
-
268
- return body , nil
373
+ return sendStreamRequest (ctx , conn , goHttp .MethodGet , nil , goHttp .StatusOK , "_api" , "cluster" , "agency-dump" )
269
374
}
270
375
271
376
type JWTAuthentication struct {
0 commit comments