-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
316 lines (267 loc) · 7.42 KB
/
main.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
// Package main is a http client that makes eportal api requests.
package main
// import modules
import (
"encoding/json"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"time"
)
// global variables
var api_username string
var api_password string
var eportal_url string
type jsonBody struct {
Count int `json:"count"`
Limit int `json:"limit"`
Offset int `json:"offset"`
Result []struct {
Checkin any `json:"checkin"`
Distro any `json:"distro"`
DistroVersion any `json:"distro_version"`
Euname any `json:"euname"`
Feed string `json:"feed"`
Hostname string `json:"hostname"`
ID any `json:"id"` // int for users, string for servers
IP string `json:"ip"`
KcareVersion any `json:"kcare_version"`
KernelID any `json:"kernel_id"`
Key string `json:"key"`
Machine any `json:"machine"`
PatchLevel any `json:"patch_level"`
PatchType any `json:"patch_type"`
Patchset string `json:"patchset"`
Processor any `json:"processor"`
Registered string `json:"registered"`
Release any `json:"release"`
Tags any `json:"tags"`
Updated any `json:"updated"`
Uptime any `json:"uptime"`
Version any `json:"version"`
Virt any `json:"virt"`
Note string `json:"note"`
Description string `json:"description"`
Products []string `json:"products"`
Limit int `json:"server_limit"`
Auto bool `json:"auto"`
Channel string `json:"channel"`
DeployAfter int `json:"deploy_after"`
Name string `json:"name"`
Status string `json:"status"`
Readonly bool `json:"readonly"`
Username string `json:"username"`
} `json:"result"`
}
// endpoint handlers
func listServers(jsonarg bool) {
var body string = setupRequest(eportal_url + "/admin/api/servers")
// output raw json
if jsonarg {
fmt.Println(body)
return
}
// decode the json to a struct
var servers jsonBody
json.Unmarshal([]byte(body), &servers)
// loop through the result array
const timefmt = "2006-01-02 15:04:05.000000"
fmt.Printf("SERVERS (%d/%d):\n", servers.Count, servers.Limit)
for _, result := range servers.Result {
fmt.Println(" IP:", result.IP)
fmt.Println(" Hostname:", result.Hostname)
fmt.Println(" ID:", result.ID)
fmt.Println(" Key:", result.Key)
// convert json string to utc date, then format
t, _ := time.Parse(timefmt, result.Registered)
f := t.Format(time.RFC822)
fmt.Println(" Registration date:", f)
// newline before next result
fmt.Println()
}
}
func listKeys(jsonarg bool) {
// make api request
var body string = setupRequest(eportal_url + "/admin/api/keys")
// output raw json
if jsonarg {
fmt.Println(body)
return
}
// decode the json to a struct
var keys jsonBody
json.Unmarshal([]byte(body), &keys)
// loop through the result array
fmt.Println("KEYS:")
for _, result := range keys.Result {
fmt.Println(" Feed:", result.Feed)
fmt.Println(" Key:", result.Key)
fmt.Println(" Description:", result.Note)
fmt.Println(" Limit:", result.Limit)
fmt.Println(" Products:")
// loop through the products array
for _, product := range result.Products {
fmt.Println(" *", product)
}
// newline before next result
fmt.Println()
}
}
func listFeeds(jsonarg bool) {
var body string = setupRequest(eportal_url + "/admin/api/feeds")
// output raw json
if jsonarg {
fmt.Println(body)
return
}
// decode the json to a struct
var feeds jsonBody
json.Unmarshal([]byte(body), &feeds)
// loop through the result array
fmt.Println("FEEDS:")
for _, result := range feeds.Result {
fmt.Println(" Name:", result.Name)
fmt.Println(" Auto:", result.Auto)
fmt.Println(" Channel:", result.Channel)
fmt.Println(" DeployAfter:", result.DeployAfter)
// newline before next result
fmt.Println()
}
}
func listPatchsets(jsonarg bool) {
var body string = setupRequest(eportal_url + "/admin/api/patchsets")
// output raw json
if jsonarg {
fmt.Println(body)
return
}
// decode the json to a struct
var patchsets jsonBody
json.Unmarshal([]byte(body), &patchsets)
// loop through the result array
fmt.Println("PATCHSETS (feed=main, product=kernel):")
for _, result := range patchsets.Result {
fmt.Printf(" %v (%v)\n", result.Patchset, result.Status)
}
// newline before next result
fmt.Println()
}
func listUsers(jsonarg bool) {
var body string = setupRequest(eportal_url + "/admin/api/users")
// output raw json
if jsonarg {
fmt.Println(body)
return
}
// decode the json to a struct
var users jsonBody
json.Unmarshal([]byte(body), &users)
// loop through the result array
var readonly, description string
fmt.Println("USERS:")
for _, result := range users.Result {
// handle readonly users
readonly = ""
if result.Readonly {
readonly = "(readonly)"
}
// handle empty descriptions
description = ""
if result.Description != "" {
description = ", " + result.Description
}
fmt.Printf(" %v: %v%v %v\n", result.ID, result.Username, description, readonly)
}
// newline before next result
fmt.Println()
}
func loadCreds() {
// find user home directory
home, _ := os.UserHomeDir()
// read credentials file
content, err := os.ReadFile(home + "/.eportal.ini")
if err != nil {
log.Fatal("Please create an ~/.eportal.ini file with 600 permissions")
}
// split creds file on newline
lines := strings.Split(string(content), "\n")
for i := 0; i < 3; i++ {
// remove whitespace around equals sign
lines[i] = strings.ReplaceAll(lines[i], " ", "")
// split into key,value pairs
line := strings.Split(lines[i], "=")
switch line[0] {
case "username":
api_username = line[1]
case "password":
api_password = line[1]
case "url":
eportal_url = line[1]
default:
log.Fatal("Unable to parse ~/.eportal.ini")
}
}
}
// api request wrapper
func setupRequest(uri string) (body string) {
// create http client
client := http.Client{Timeout: 5 * time.Second}
// construct http request
req, err := http.NewRequest(http.MethodGet, uri, http.NoBody)
if err != nil {
log.Fatal(err)
}
// add basic auth headers
req.SetBasicAuth(api_username, api_password)
// make request
res, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
// read the http body
defer res.Body.Close()
resBody, err := io.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
// return the body of the request as a json string
return string(resBody)
}
func main() {
// parse cli arguments
var serversarg, keysarg, feedsarg, usersarg, patchsetsarg, jsonarg bool
flag.BoolVar(&serversarg, "servers", false, "--servers")
flag.BoolVar(&keysarg, "keys", false, "--keys")
flag.BoolVar(&feedsarg, "feeds", false, "--feeds")
flag.BoolVar(&usersarg, "users", false, "--users")
flag.BoolVar(&patchsetsarg, "patchsets", false, "--patchsets")
flag.BoolVar(&jsonarg, "json", false, "--json")
flag.Parse()
// check we have at least 1 argument
if len(os.Args) < 2 {
fmt.Println("Usage: eportal-go --<servers|keys|feeds|users|patchsets> [--json]")
os.Exit(1)
}
// load credentials from file
loadCreds()
// call handlers for valid endpoints
if serversarg {
listServers(jsonarg)
}
if keysarg {
listKeys(jsonarg)
}
if feedsarg {
listFeeds(jsonarg)
}
if usersarg {
listUsers(jsonarg)
}
if patchsetsarg {
listPatchsets(jsonarg)
}
}