/
kit.go
266 lines (233 loc) · 8.14 KB
/
kit.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
/*************************************************************************
* Copyright 2021 Gravwell, Inc. All rights reserved.
* Contact: <legal@gravwell.io>
*
* This software may be modified and distributed under the terms of the
* BSD 2-clause license. See the LICENSE file for details.
**************************************************************************/
package client
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"os"
"github.com/gravwell/gravwell/v3/client/types"
"github.com/google/uuid"
)
const (
minKitSize int64 = 128
)
var (
ErrInvalidKitSize = errors.New("Kit is too small to upload")
)
// UploadKit stages a kit file for installation. The parameter 'p' should
// be the path of a kit file on disk. A KitState object containing information
// about the kit is returned on success.
func (c *Client) UploadKit(p string) (pc types.KitState, err error) {
var fin *os.File
var fi os.FileInfo
var mp io.Writer
var req *http.Request
//open the file
if fin, err = os.Open(p); err != nil {
return
}
if fi, err = fin.Stat(); err != nil {
fin.Close()
return
}
if fi.Size() <= 0 {
fin.Close()
err = errors.New("License file is empty")
return
}
bb := bytes.NewBuffer(nil)
wtr := multipart.NewWriter(bb)
if mp, err = wtr.CreateFormFile(`file`, `package`); err != nil {
fin.Close()
return
}
if _, err = io.Copy(mp, fin); err != nil {
fin.Close()
return
}
if err = wtr.Close(); err != nil {
fin.Close()
return
}
if err = fin.Close(); err != nil {
return
}
uri := fmt.Sprintf("%s://%s%s", c.httpScheme, c.server, kitUrl())
if req, err = http.NewRequest(http.MethodPost, uri, bb); err != nil {
return
}
req.Header.Set(`Content-Type`, wtr.FormDataContentType())
okResps := []int{http.StatusOK, http.StatusMultiStatus}
err = c.staticRequest(req, &pc, okResps)
return
}
// PullKit tells the webserver to stage the kit with the specified GUID for installation,
// pulling the kit from the kit server. A KitState object containing information
// about the kit is returned on success.
func (c *Client) PullKit(guid uuid.UUID) (pc types.KitState, err error) {
var mp io.Writer
var req *http.Request
bb := bytes.NewBuffer(nil)
wtr := multipart.NewWriter(bb)
if mp, err = wtr.CreateFormField(`remote`); err != nil {
return
}
if _, err = mp.Write([]byte(guid.String())); err != nil {
return
}
if err = wtr.Close(); err != nil {
return
}
uri := fmt.Sprintf("%s://%s%s", c.httpScheme, c.server, kitUrl())
if req, err = http.NewRequest(http.MethodPost, uri, bb); err != nil {
return
}
req.Header.Set(`Content-Type`, wtr.FormDataContentType())
okResps := []int{http.StatusOK, http.StatusMultiStatus}
err = c.staticRequest(req, &pc, okResps)
return
}
// ListRemoteKits returns a list of kits available on the kit server.
func (c *Client) ListRemoteKits(all bool) (mds []types.KitMetadata, err error) {
err = c.getStaticURL(remoteKitUrl(all), &mds)
return
}
// ListKits returns a list of all installed and staged kits.
func (c *Client) ListKits() (pkgs []types.IdKitState, err error) {
err = c.getStaticURL(kitUrl(), &pkgs)
return
}
// KitInfo returns information about a particular installed/staged kit, specified
// by the kit's UUID.
func (c *Client) KitInfo(id uuid.UUID) (ki types.IdKitState, err error) {
err = c.getStaticURL(kitIdUrl(id.String()), &ki)
return
}
// InstallKit tells the webserver to install a staged kit. The id parameter
// is the UUID of the staged kit. The cfg parameter provides install-time
// options.
func (c *Client) InstallKit(id string, cfg types.KitConfig) (err error) {
err = c.putStaticURL(kitIdUrl(id), cfg)
return
}
// ModifyKit tells the webserver to change parameters on an installed kit.
// The id parameter is the UUID of the installed kit. The cfg parameter provides
// the desired changes, with the following fields being respected: Global, InstallationGroup,
// and Labels.
func (c *Client) ModifyKit(id string, cfg types.KitConfig) (report types.KitModifyReport, err error) {
err = c.methodStaticPushURL(http.MethodPatch, kitIdUrl(id), cfg, &report)
return
}
// DeleteKit uninstalls a kit (specified by UUID). Note that if kit items
// have been modified, DeleteKit will return an error; use ForceDeleteKit to
// remove the kit regardless.
func (c *Client) DeleteKit(id string) (err error) {
err = c.deleteStaticURL(kitIdUrl(id), nil)
return
}
// DeleteKitEx attempts to uninstall a kit. If kit items have been modified,
// it will return an error and a list of modified items. If nothing has been
// changed, it returns an empty list and a nil error.
func (c *Client) DeleteKitEx(id string) ([]types.SourcedKitItem, error) {
var resp *http.Response
var err error
resp, err = c.methodRequestURL(http.MethodDelete, kitIdUrl(id), ``, nil)
if err != nil {
// this means we weren't able to get a request to the server, return the error
return []types.SourcedKitItem{}, err
}
defer drainResponse(resp)
if resp.StatusCode != http.StatusOK {
// There are basically two kinds of errors:
// 1. Kit items have been modified; body contains a list of modified items
// 2. Other errors (kit doesn't exist, malformed ID, etc.)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return []types.SourcedKitItem{}, err
}
var ks struct {
ModifiedItems []types.SourcedKitItem
Error string
}
if err := json.Unmarshal(body, &ks); err != nil {
// This was a type 2 error: "something else"
return []types.SourcedKitItem{}, fmt.Errorf("Bad status %v: %v", resp.Status, string(body))
}
return ks.ModifiedItems, errors.New(ks.Error)
}
// Success, the kit should be deleted now.
return []types.SourcedKitItem{}, nil
}
// AdminDeleteKit is an admin-only function which can delete a kit owned by
// any user.
func (c *Client) AdminDeleteKit(id string) (err error) {
c.SetAdminMode()
err = c.deleteStaticURL(kitIdUrl(id), nil)
c.ClearAdminMode()
return
}
// ForceDeleteKit uninstalls a kit (specified by UUID) regardless of any
// changes made since installation.
func (c *Client) ForceDeleteKit(id string) (err error) {
params := map[string]string{
"force": "true",
}
err = c.methodStaticParamURL(http.MethodDelete, kitIdUrl(id), params, nil)
return
}
// BuildKit builds a new kit. The parameter 'pbr' contains information about
// the kit to be built, including lists of objects to include. On success, the
// returned KitBuildResponse will contain a UUID which can be used to download
// the kit via the KitDownloadRequest function.
func (c *Client) BuildKit(pbr types.KitBuildRequest) (r types.KitBuildResponse, err error) {
err = c.postStaticURL(kitBuildUrl(), pbr, &r)
return
}
// DeleteBuildKit removes a recently-built kit.
func (c *Client) DeleteBuildKit(id string) (err error) {
err = c.deleteStaticURL(kitDownloadUrl(id), nil)
return
}
// KitDownloadRequest initiates a download for the specified kit and returns
// the associated http.Response structure. The kit is available in the Body
// field of the response.
func (c *Client) KitDownloadRequest(id string) (*http.Response, error) {
return c.DownloadRequest(kitDownloadUrl(id))
}
// AdminListKits is an admin-only function which lists all kits on the system.
// Non-administrators will get the same list as returned by ListKits.
func (c *Client) AdminListKits() (pkgs []types.IdKitState, err error) {
c.SetAdminMode()
if err = c.getStaticURL(kitUrl(), &pkgs); err != nil {
pkgs = nil
}
c.ClearAdminMode()
return
}
// KitStatuses returns the statuses of any ongoing or completed kit installations.
func (c *Client) KitStatuses() (statuses []types.InstallStatus, err error) {
err = c.getStaticURL(kitStatusUrl(), &statuses)
return
}
// ListKitBuildHistory returns KitBuildRequests for all kits previously built by the
// user. Note that only the most recent build request is stored for each unique
// kit ID (e.g. "io.gravwell.foo").
func (c *Client) ListKitBuildHistory() (hist []types.KitBuildRequest, err error) {
err = c.getStaticURL(kitBuildHistoryUrl(), &hist)
return
}
// DeleteKitBuildHistory deletes a build history entry for the given ID e.g. "io.gravwell.foo"
func (c *Client) DeleteKitBuildHistory(id string) error {
return c.deleteStaticURL(kitDeleteBuildHistoryUrl(id), nil)
}