@@ -43,6 +43,8 @@ import (
4343
4444 api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
4545 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"
4648 "github.com/arangodb/kube-arangodb/pkg/util/constants"
4749 "github.com/arangodb/kube-arangodb/pkg/util/errors"
4850 "github.com/arangodb/kube-arangodb/pkg/util/globals"
@@ -51,76 +53,149 @@ import (
5153 "github.com/arangodb/kube-arangodb/pkg/util/kclient"
5254)
5355
54- const ArgDeploymentName = "deployment-name"
56+ const (
57+ ArgDeploymentName = "deployment-name"
58+ ArgMemberName = "member-name"
59+ ArgAcceptedCode = "accepted-code"
60+ )
5561
5662func init () {
57- var deploymentName string
58-
5963 cmdMain .AddCommand (cmdAdmin )
60- cmdAdmin .AddCommand (cmdAgency )
64+ cmdAdmin .AddCommand (cmdAdminAgency )
65+ cmdAdmin .AddCommand (cmdAdminMember )
6166
62- cmdAgency .AddCommand (cmdAgencyDump )
63- cmdAgencyDump .Flags ().StringVarP ( & deploymentName , ArgDeploymentName , "d" , "" ,
67+ cmdAdminAgency .AddCommand (cmdAdminAgencyDump )
68+ cmdAdminAgencyDump .Flags ().StringP ( ArgDeploymentName , "d" , "" ,
6469 "necessary when more than one deployment exist within on namespace" )
6570
66- cmdAgency .AddCommand (cmdAgencyState )
67- cmdAgencyState .Flags ().StringVarP ( & deploymentName , ArgDeploymentName , "d" , "" ,
71+ cmdAdminAgency .AddCommand (cmdAdminAgencyState )
72+ cmdAdminAgencyState .Flags ().StringP ( ArgDeploymentName , "d" , "" ,
6873 "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" )
6983}
7084
7185var cmdAdmin = & cobra.Command {
7286 Use : "admin" ,
7387 Short : "Administration operations" ,
74- Run : adminShowUsage ,
88+ RunE : cli . Usage ,
7589}
7690
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 {
78110 Use : "agency" ,
79111 Short : "Agency operations" ,
80- Run : agencyShowUsage ,
112+ RunE : cli . Usage ,
81113}
82114
83- var cmdAgencyDump = & cobra.Command {
115+ var cmdAdminAgencyDump = & cobra.Command {
84116 Use : "dump" ,
85117 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 ,
88120}
89121
90- var cmdAgencyState = & cobra.Command {
122+ var cmdAdminAgencyState = & cobra.Command {
91123 Use : "state" ,
92124 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 ,
95127}
96128
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+ }
100167
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
103171}
104172
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+ }
107178 ctx := getInterruptionContext ()
108179 d , certCA , auth , err := getDeploymentAndCredentials (ctx , deploymentName )
109180 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
111183 }
112184
113185 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 (),
115187 d .GetName ())
188+ logger .Err (err ).Error ("Invalid deployment type" )
189+ return err
116190 }
117191
118192 dnsName := k8sutil .CreatePodDNSName (d .GetObjectMeta (), api .ServerGroupAgents .AsRole (), d .Status .Members .Agents [0 ].ID )
119193 endpoint := getArangoEndpoint (d .GetAcceptedSpec ().IsSecure (), dnsName )
120194 conn := createClient ([]string {endpoint }, certCA , auth , connection .ApplicationJSON )
121195 leaderID , err := getAgencyLeader (ctx , conn )
122196 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
124199 }
125200
126201 dnsLeaderName := k8sutil .CreatePodDNSName (d .GetObjectMeta (), api .ServerGroupAgents .AsRole (), leaderID )
@@ -131,24 +206,32 @@ func cmdGetAgencyState(cmd *cobra.Command, _ []string) {
131206 defer body .Close ()
132207 }
133208 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
135211 }
136212
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
139216}
140217
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+ }
143223 ctx := getInterruptionContext ()
144224 d , certCA , auth , err := getDeploymentAndCredentials (ctx , deploymentName )
145225 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
147228 }
148229
149230 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 (),
151232 d .GetName ())
233+ logger .Err (err ).Error ("Invalid deployment type" )
234+ return err
152235 }
153236
154237 endpoint := getArangoEndpoint (d .GetAcceptedSpec ().IsSecure (), k8sutil .CreateDatabaseClientServiceDNSName (d .GetObjectMeta ()))
@@ -158,26 +241,62 @@ func cmdGetAgencyDump(cmd *cobra.Command, _ []string) {
158241 defer body .Close ()
159242 }
160243 if err != nil {
161- logger .Err (err ).Fatal ("can not get dump" )
244+ logger .Err (err ).Error ("can not get dump" )
245+ return err
162246 }
163247
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
166251}
167252
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 ... )
173264 if err != nil {
174265 return nil , err
175266 }
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 ()))
178292 }
179293
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" )
181300}
182301
183302// getDeploymentAndCredentials returns deployment and necessary credentials to communicate with ArangoDB pods.
@@ -235,15 +354,10 @@ func getArangoEndpoint(secure bool, dnsName string) string {
235354
236355// getAgencyLeader returns the leader ID of the agency.
237356func 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" )
241358 if err != nil {
242359 return "" , err
243360 }
244- if resp .Code () != goHttp .StatusOK {
245- return "" , errors .New ("unexpected HTTP status from agency-dump endpoint" )
246- }
247361
248362 if leaderID , ok := output ["leaderId" ]; ok {
249363 if id , ok := leaderID .(string ); ok {
@@ -256,16 +370,7 @@ func getAgencyLeader(ctx context.Context, conn connection.Connection) (string, e
256370
257371// getAgencyDump returns dump of the agency.
258372func 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" )
269374}
270375
271376type JWTAuthentication struct {
0 commit comments