feat(pkger): extend HTTP API to enable users to submit multiple pkgs in one call

pull/16788/head
Johnny Steenbergen 2020-02-06 20:57:24 -08:00 committed by Johnny Steenbergen
parent ec13042c57
commit 684f7b82ad
3 changed files with 161 additions and 19 deletions

View File

@ -7145,18 +7145,24 @@ components:
type: string
package:
$ref: "#/components/schemas/Pkg"
packages:
type: array
items:
$ref: "#/components/schemas/Pkg"
secrets:
type: object
additionalProperties:
type: string
remote:
type: object
properties:
url:
type: string
contentType:
type: string
required: ["url"]
remotes:
type: array
items:
type: object
properties:
url:
type: string
contentType:
type: string
required: ["url"]
PkgCreate:
type: object
properties:

View File

@ -152,19 +152,45 @@ func (p PkgRemote) Encoding() Encoding {
type ReqApplyPkg struct {
DryRun bool `json:"dryRun" yaml:"dryRun"`
OrgID string `json:"orgID" yaml:"orgID"`
Remote PkgRemote `json:"remote" yaml:"remote"`
Remotes []PkgRemote `json:"remotes" yaml:"remotes"`
RawPkgs []json.RawMessage `json:"packages" yaml:"packages"`
RawPkg json.RawMessage `json:"package" yaml:"package"`
EnvRefs map[string]string `json:"envRefs"`
Secrets map[string]string `json:"secrets"`
}
// Pkg returns a pkg parsed and validated from the RawPkg field.
func (r ReqApplyPkg) Pkg(encoding Encoding) (*Pkg, error) {
if r.Remote.URL != "" {
return Parse(r.Remote.Encoding(), FromHTTPRequest(r.Remote.URL))
// Pkgs returns all pkgs associated with the request.
func (r ReqApplyPkg) Pkgs(encoding Encoding) (*Pkg, error) {
var rawPkgs []*Pkg
for _, rem := range r.Remotes {
if rem.URL == "" {
continue
}
pkg, err := Parse(rem.Encoding(), FromHTTPRequest(rem.URL), ValidSkipParseError())
if err != nil {
return nil, &influxdb.Error{
Code: influxdb.EUnprocessableEntity,
Msg: fmt.Sprintf("pkg from url[%s] had an issue: %s", rem.URL, err.Error()),
}
}
rawPkgs = append(rawPkgs, pkg)
}
return Parse(encoding, FromReader(bytes.NewReader(r.RawPkg)))
for i, rawPkg := range append(r.RawPkgs, r.RawPkg) {
if rawPkg == nil {
continue
}
pkg, err := Parse(encoding, FromReader(bytes.NewReader(rawPkg)), ValidSkipParseError())
if err != nil {
return nil, &influxdb.Error{
Code: influxdb.EUnprocessableEntity,
Msg: fmt.Sprintf("pkg [%d] had an issue: %s", i, err.Error()),
}
}
rawPkgs = append(rawPkgs, pkg)
}
return Combine(rawPkgs...)
}
// RespApplyPkg is the response body for the apply pkg endpoint.
@ -199,10 +225,10 @@ func (s *HTTPServer) applyPkg(w http.ResponseWriter, r *http.Request) {
}
userID := auth.GetUserID()
parsedPkg, err := reqBody.Pkg(encoding)
parsedPkg, err := reqBody.Pkgs(encoding)
if err != nil {
s.api.Err(w, &influxdb.Error{
Code: influxdb.EInvalid,
Code: influxdb.EUnprocessableEntity,
Err: err,
})
return
@ -302,7 +328,7 @@ func convertParseErr(err error) []ValidationErr {
func newDecodeErr(encoding string, err error) *influxdb.Error {
return &influxdb.Error{
Msg: fmt.Sprintf("unable to unmarshal %s; Err: %v", encoding, err),
Msg: fmt.Sprintf("unable to unmarshal %s", encoding),
Code: influxdb.EInvalid,
Err: err,
}

View File

@ -103,9 +103,18 @@ func TestPkgerHTTPServer(t *testing.T) {
reqBody: pkger.ReqApplyPkg{
DryRun: true,
OrgID: influxdb.ID(9000).String(),
Remote: pkger.PkgRemote{
Remotes: []pkger.PkgRemote{{
URL: "https://gist.githubusercontent.com/jsteenb2/3a3b2b5fcbd6179b2494c2b54aa2feb0/raw/989d361db7a851a3c388eaed0b59dce7fca7fdf3/bucket_pkg.json",
},
}},
},
},
{
name: "app jsonnet",
contentType: "application/x-jsonnet",
reqBody: pkger.ReqApplyPkg{
DryRun: true,
OrgID: influxdb.ID(9000).String(),
RawPkg: bucketPkgKinds(t, pkger.EncodingJsonnet),
},
},
{
@ -213,6 +222,107 @@ func TestPkgerHTTPServer(t *testing.T) {
t.Run(tt.name, fn)
}
})
t.Run("with multiple pkgs", func(t *testing.T) {
newBktPkg := func(t *testing.T, bktName string) json.RawMessage {
t.Helper()
pkgStr := fmt.Sprintf(`[
{
"apiVersion": "%[1]s",
"kind": "Bucket",
"metadata": {
"name": %q
},
"spec": {}
}
]`, pkger.APIVersion, bktName)
pkg, err := pkger.Parse(pkger.EncodingJSON, pkger.FromString(pkgStr))
require.NoError(t, err)
pkgBytes, err := pkg.Encode(pkger.EncodingJSON)
require.NoError(t, err)
return pkgBytes
}
tests := []struct {
name string
reqBody pkger.ReqApplyPkg
expectedBkts []string
}{
{
name: "retrieves package from a URL and raw pkgs",
reqBody: pkger.ReqApplyPkg{
DryRun: true,
OrgID: influxdb.ID(9000).String(),
Remotes: []pkger.PkgRemote{{
ContentType: "json",
URL: "https://gist.githubusercontent.com/jsteenb2/3a3b2b5fcbd6179b2494c2b54aa2feb0/raw/989d361db7a851a3c388eaed0b59dce7fca7fdf3/bucket_pkg.json",
}},
RawPkgs: []json.RawMessage{
newBktPkg(t, "bkt1"),
newBktPkg(t, "bkt2"),
newBktPkg(t, "bkt3"),
},
},
expectedBkts: []string{"bkt1", "bkt2", "bkt3", "rucket_11"},
},
{
name: "retrieves packages from raw single and list",
reqBody: pkger.ReqApplyPkg{
DryRun: true,
OrgID: influxdb.ID(9000).String(),
RawPkg: newBktPkg(t, "bkt4"),
RawPkgs: []json.RawMessage{
newBktPkg(t, "bkt1"),
newBktPkg(t, "bkt2"),
newBktPkg(t, "bkt3"),
},
},
expectedBkts: []string{"bkt1", "bkt2", "bkt3", "bkt4"},
},
}
for _, tt := range tests {
fn := func(t *testing.T) {
svc := &fakeSVC{
DryRunFn: func(ctx context.Context, orgID, userID influxdb.ID, pkg *pkger.Pkg, opts ...pkger.ApplyOptFn) (pkger.Summary, pkger.Diff, error) {
if err := pkg.Validate(); err != nil {
return pkger.Summary{}, pkger.Diff{}, err
}
sum := pkg.Summary()
var diff pkger.Diff
for _, b := range sum.Buckets {
diff.Buckets = append(diff.Buckets, pkger.DiffBucket{
Name: b.Name,
})
}
return sum, diff, nil
},
}
pkgHandler := pkger.NewHTTPServer(zap.NewNop(), svc)
svr := newMountedHandler(pkgHandler, 1)
testttp.
PostJSON(t, "/api/v2/packages/apply", tt.reqBody).
Do(svr).
ExpectStatus(http.StatusOK).
ExpectBody(func(buf *bytes.Buffer) {
var resp pkger.RespApplyPkg
decodeBody(t, buf, &resp)
require.Len(t, resp.Summary.Buckets, len(tt.expectedBkts))
for i, expected := range tt.expectedBkts {
assert.Equal(t, expected, resp.Summary.Buckets[i].Name)
}
})
}
t.Run(tt.name, fn)
}
})
})
t.Run("apply a pkg", func(t *testing.T) {