2020-02-03 19:22:47 +00:00
|
|
|
package pkger
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-06-17 22:52:58 +00:00
|
|
|
"encoding/json"
|
2020-02-03 19:22:47 +00:00
|
|
|
"net/http"
|
|
|
|
|
2020-04-03 17:39:20 +00:00
|
|
|
"github.com/influxdata/influxdb/v2"
|
2020-06-29 23:38:52 +00:00
|
|
|
ihttp "github.com/influxdata/influxdb/v2/http"
|
2020-04-03 17:39:20 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/pkg/httpc"
|
2020-02-03 19:22:47 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// HTTPRemoteService provides an http client that is fluent in all things pkger.
|
|
|
|
type HTTPRemoteService struct {
|
|
|
|
Client *httpc.Client
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ SVC = (*HTTPRemoteService)(nil)
|
|
|
|
|
2020-03-20 23:20:53 +00:00
|
|
|
func (s *HTTPRemoteService) InitStack(ctx context.Context, userID influxdb.ID, stack Stack) (Stack, error) {
|
2020-03-23 23:51:43 +00:00
|
|
|
reqBody := ReqCreateStack{
|
|
|
|
OrgID: stack.OrgID.String(),
|
|
|
|
Name: stack.Name,
|
2020-03-26 20:23:14 +00:00
|
|
|
Description: stack.Description,
|
2020-06-26 22:12:57 +00:00
|
|
|
URLs: stack.TemplateURLs,
|
2020-03-23 23:51:43 +00:00
|
|
|
}
|
|
|
|
|
2020-06-17 03:12:37 +00:00
|
|
|
var respBody RespStack
|
2020-03-23 23:51:43 +00:00
|
|
|
err := s.Client.
|
2020-06-29 21:25:51 +00:00
|
|
|
PostJSON(reqBody, RoutePrefixStacks).
|
2020-03-23 23:51:43 +00:00
|
|
|
DecodeJSON(&respBody).
|
|
|
|
Do(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return Stack{}, err
|
|
|
|
}
|
|
|
|
|
2020-06-17 03:12:37 +00:00
|
|
|
return convertRespStackToStack(respBody)
|
2020-03-20 02:08:35 +00:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:40:32 +00:00
|
|
|
func (s *HTTPRemoteService) DeleteStack(ctx context.Context, identifiers struct{ OrgID, UserID, StackID influxdb.ID }) error {
|
|
|
|
return s.Client.
|
2020-06-29 21:25:51 +00:00
|
|
|
Delete(RoutePrefixStacks, identifiers.StackID.String()).
|
2020-05-01 21:40:32 +00:00
|
|
|
QueryParams([2]string{"orgID", identifiers.OrgID.String()}).
|
|
|
|
Do(ctx)
|
|
|
|
}
|
|
|
|
|
2020-04-30 06:37:39 +00:00
|
|
|
func (s *HTTPRemoteService) ListStacks(ctx context.Context, orgID influxdb.ID, f ListFilter) ([]Stack, error) {
|
|
|
|
queryParams := [][2]string{{"orgID", orgID.String()}}
|
|
|
|
for _, name := range f.Names {
|
|
|
|
queryParams = append(queryParams, [2]string{"name", name})
|
|
|
|
}
|
|
|
|
for _, stackID := range f.StackIDs {
|
|
|
|
queryParams = append(queryParams, [2]string{"stackID", stackID.String()})
|
|
|
|
}
|
|
|
|
|
|
|
|
var resp RespListStacks
|
|
|
|
err := s.Client.
|
2020-06-29 21:25:51 +00:00
|
|
|
Get(RoutePrefixStacks).
|
2020-04-30 06:37:39 +00:00
|
|
|
QueryParams(queryParams...).
|
|
|
|
DecodeJSON(&resp).
|
|
|
|
Do(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-06-17 03:12:37 +00:00
|
|
|
|
|
|
|
out := make([]Stack, 0, len(resp.Stacks))
|
|
|
|
for _, st := range resp.Stacks {
|
|
|
|
stack, err := convertRespStackToStack(st)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
out = append(out, stack)
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *HTTPRemoteService) ReadStack(ctx context.Context, id influxdb.ID) (Stack, error) {
|
|
|
|
var respBody RespStack
|
|
|
|
err := s.Client.
|
2020-06-29 21:25:51 +00:00
|
|
|
Get(RoutePrefixStacks, id.String()).
|
2020-06-17 03:12:37 +00:00
|
|
|
DecodeJSON(&respBody).
|
|
|
|
Do(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return Stack{}, err
|
|
|
|
}
|
|
|
|
return convertRespStackToStack(respBody)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *HTTPRemoteService) UpdateStack(ctx context.Context, upd StackUpdate) (Stack, error) {
|
|
|
|
reqBody := ReqUpdateStack{
|
2020-06-26 20:58:22 +00:00
|
|
|
Name: upd.Name,
|
|
|
|
Description: upd.Description,
|
2020-06-26 22:12:57 +00:00
|
|
|
TemplateURLs: upd.TemplateURLs,
|
2020-06-26 20:58:22 +00:00
|
|
|
}
|
|
|
|
for _, r := range upd.AdditionalResources {
|
|
|
|
reqBody.AdditionalResources = append(reqBody.AdditionalResources, ReqUpdateStackResource{
|
|
|
|
ID: r.ID.String(),
|
|
|
|
MetaName: r.MetaName,
|
|
|
|
Kind: r.Kind,
|
|
|
|
})
|
2020-06-17 03:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var respBody RespStack
|
|
|
|
err := s.Client.
|
2020-06-29 21:25:51 +00:00
|
|
|
PatchJSON(reqBody, RoutePrefixStacks, upd.ID.String()).
|
2020-06-17 03:12:37 +00:00
|
|
|
DecodeJSON(&respBody).
|
|
|
|
Do(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return Stack{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return convertRespStackToStack(respBody)
|
2020-04-30 06:37:39 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 04:41:04 +00:00
|
|
|
// Export will produce a pkg from the parameters provided.
|
|
|
|
func (s *HTTPRemoteService) Export(ctx context.Context, opts ...ExportOptFn) (*Pkg, error) {
|
|
|
|
opt, err := exportOptFromOptFns(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-02-03 19:22:47 +00:00
|
|
|
}
|
2020-03-06 18:52:18 +00:00
|
|
|
|
2020-06-25 18:54:42 +00:00
|
|
|
var orgIDs []ReqExportOrgIDOpt
|
2020-03-06 18:52:18 +00:00
|
|
|
for _, org := range opt.OrgIDs {
|
2020-06-25 18:54:42 +00:00
|
|
|
orgIDs = append(orgIDs, ReqExportOrgIDOpt{
|
2020-03-06 18:52:18 +00:00
|
|
|
OrgID: org.OrgID.String(),
|
|
|
|
Filters: struct {
|
|
|
|
ByLabel []string `json:"byLabel"`
|
|
|
|
ByResourceKind []Kind `json:"byResourceKind"`
|
|
|
|
}{
|
|
|
|
ByLabel: org.LabelNames,
|
|
|
|
ByResourceKind: org.ResourceKinds,
|
|
|
|
},
|
|
|
|
})
|
2020-02-03 19:22:47 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 18:54:42 +00:00
|
|
|
reqBody := ReqExport{
|
2020-06-26 19:41:44 +00:00
|
|
|
StackID: opt.StackID.String(),
|
|
|
|
OrgIDs: orgIDs,
|
|
|
|
Resources: opt.Resources,
|
2020-02-03 19:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var newPkg *Pkg
|
2020-06-25 04:41:04 +00:00
|
|
|
err = s.Client.
|
2020-06-29 21:25:51 +00:00
|
|
|
PostJSON(reqBody, RoutePrefixTemplates, "/export").
|
2020-02-03 19:22:47 +00:00
|
|
|
Decode(func(resp *http.Response) error {
|
2020-06-15 21:13:38 +00:00
|
|
|
pkg, err := Parse(EncodingJSON, FromReader(resp.Body, "export"))
|
2020-02-03 19:22:47 +00:00
|
|
|
newPkg = pkg
|
|
|
|
return err
|
|
|
|
}).
|
|
|
|
Do(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := newPkg.Validate(ValidWithoutResources()); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return newPkg, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DryRun provides a dry run of the pkg application. The pkg will be marked verified
|
|
|
|
// for later calls to Apply. This func will be run on an Apply if it has not been run
|
|
|
|
// already.
|
2020-06-25 04:46:03 +00:00
|
|
|
func (s *HTTPRemoteService) DryRun(ctx context.Context, orgID, userID influxdb.ID, opts ...ApplyOptFn) (ImpactSummary, error) {
|
2020-06-15 21:13:38 +00:00
|
|
|
return s.apply(ctx, orgID, true, opts...)
|
2020-02-03 19:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply will apply all the resources identified in the provided pkg. The entire pkg will be applied
|
|
|
|
// in its entirety. If a failure happens midway then the entire pkg will be rolled back to the state
|
|
|
|
// from before the pkg was applied.
|
2020-06-25 04:46:03 +00:00
|
|
|
func (s *HTTPRemoteService) Apply(ctx context.Context, orgID, userID influxdb.ID, opts ...ApplyOptFn) (ImpactSummary, error) {
|
2020-06-15 21:13:38 +00:00
|
|
|
return s.apply(ctx, orgID, false, opts...)
|
2020-02-06 05:42:01 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 04:46:03 +00:00
|
|
|
func (s *HTTPRemoteService) apply(ctx context.Context, orgID influxdb.ID, dryRun bool, opts ...ApplyOptFn) (ImpactSummary, error) {
|
2020-04-01 00:01:45 +00:00
|
|
|
opt := applyOptFromOptFns(opts...)
|
2020-02-03 19:22:47 +00:00
|
|
|
|
2020-06-25 18:54:42 +00:00
|
|
|
var rawPkg ReqRawTemplate
|
2020-06-15 21:13:38 +00:00
|
|
|
for _, pkg := range opt.Pkgs {
|
2020-04-29 22:24:19 +00:00
|
|
|
b, err := pkg.Encode(EncodingJSON)
|
|
|
|
if err != nil {
|
2020-06-25 04:46:03 +00:00
|
|
|
return ImpactSummary{}, err
|
2020-04-29 22:24:19 +00:00
|
|
|
}
|
2020-06-15 21:13:38 +00:00
|
|
|
rawPkg.Pkg = b
|
|
|
|
rawPkg.Sources = pkg.sources
|
|
|
|
rawPkg.ContentType = EncodingJSON.String()
|
2020-02-03 19:22:47 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 18:54:42 +00:00
|
|
|
reqBody := ReqApply{
|
2020-06-15 21:13:38 +00:00
|
|
|
OrgID: orgID.String(),
|
|
|
|
DryRun: dryRun,
|
|
|
|
EnvRefs: opt.EnvRefs,
|
|
|
|
Secrets: opt.MissingSecrets,
|
|
|
|
RawTemplate: rawPkg,
|
2020-02-03 19:22:47 +00:00
|
|
|
}
|
2020-04-01 00:01:45 +00:00
|
|
|
if opt.StackID != 0 {
|
|
|
|
stackID := opt.StackID.String()
|
|
|
|
reqBody.StackID = &stackID
|
|
|
|
}
|
2020-02-03 19:22:47 +00:00
|
|
|
|
2020-06-17 22:52:58 +00:00
|
|
|
for act := range opt.ResourcesToSkip {
|
|
|
|
b, err := json.Marshal(act)
|
|
|
|
if err != nil {
|
2020-06-25 04:46:03 +00:00
|
|
|
return ImpactSummary{}, influxErr(influxdb.EInvalid, err)
|
2020-06-17 22:52:58 +00:00
|
|
|
}
|
|
|
|
reqBody.RawActions = append(reqBody.RawActions, ReqRawAction{
|
|
|
|
Action: string(ActionTypeSkipResource),
|
|
|
|
Properties: b,
|
|
|
|
})
|
|
|
|
}
|
2020-06-18 13:47:07 +00:00
|
|
|
for kind := range opt.KindsToSkip {
|
|
|
|
b, err := json.Marshal(ActionSkipKind{Kind: kind})
|
|
|
|
if err != nil {
|
2020-06-25 04:46:03 +00:00
|
|
|
return ImpactSummary{}, influxErr(influxdb.EInvalid, err)
|
2020-06-18 13:47:07 +00:00
|
|
|
}
|
|
|
|
reqBody.RawActions = append(reqBody.RawActions, ReqRawAction{
|
|
|
|
Action: string(ActionTypeSkipKind),
|
|
|
|
Properties: b,
|
|
|
|
})
|
|
|
|
}
|
2020-06-17 22:52:58 +00:00
|
|
|
|
2020-06-25 18:54:42 +00:00
|
|
|
var resp RespApply
|
2020-04-29 22:24:19 +00:00
|
|
|
err := s.Client.
|
2020-06-29 21:25:51 +00:00
|
|
|
PostJSON(reqBody, RoutePrefixTemplates, "/apply").
|
2020-02-03 19:22:47 +00:00
|
|
|
DecodeJSON(&resp).
|
2020-06-29 23:38:52 +00:00
|
|
|
StatusFn(func(resp *http.Response) error {
|
|
|
|
// valid response code when the template itself has parser errors.
|
|
|
|
// we short circuit on that and allow that response to pass through
|
|
|
|
// but consume the initial implementation if that does not hold.
|
|
|
|
if resp.StatusCode == http.StatusUnprocessableEntity {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return ihttp.CheckError(resp)
|
|
|
|
}).
|
2020-02-03 19:22:47 +00:00
|
|
|
Do(ctx)
|
|
|
|
if err != nil {
|
2020-06-25 04:46:03 +00:00
|
|
|
return ImpactSummary{}, err
|
2020-02-03 19:22:47 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 04:46:03 +00:00
|
|
|
impact := ImpactSummary{
|
2020-06-15 21:13:38 +00:00
|
|
|
Sources: resp.Sources,
|
2020-05-28 22:09:30 +00:00
|
|
|
Diff: resp.Diff,
|
|
|
|
Summary: resp.Summary,
|
2020-05-28 23:30:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if stackID, err := influxdb.IDFromString(resp.StackID); err == nil {
|
|
|
|
impact.StackID = *stackID
|
|
|
|
}
|
|
|
|
|
|
|
|
return impact, NewParseError(resp.Errors...)
|
2020-02-03 19:22:47 +00:00
|
|
|
}
|
2020-06-17 03:12:37 +00:00
|
|
|
|
|
|
|
func convertRespStackToStack(respStack RespStack) (Stack, error) {
|
|
|
|
newStack := Stack{
|
2020-06-26 22:12:57 +00:00
|
|
|
Name: respStack.Name,
|
|
|
|
Description: respStack.Description,
|
|
|
|
Sources: respStack.Sources,
|
|
|
|
TemplateURLs: respStack.URLs,
|
|
|
|
Resources: make([]StackResource, 0, len(respStack.Resources)),
|
|
|
|
CRUDLog: respStack.CRUDLog,
|
2020-06-17 03:12:37 +00:00
|
|
|
}
|
2020-06-24 18:27:03 +00:00
|
|
|
for _, r := range respStack.Resources {
|
|
|
|
sr := StackResource{
|
2020-06-26 03:17:11 +00:00
|
|
|
APIVersion: r.APIVersion,
|
|
|
|
MetaName: r.MetaName,
|
|
|
|
Kind: r.Kind,
|
|
|
|
}
|
|
|
|
for _, a := range r.Associations {
|
|
|
|
sra := StackResourceAssociation{
|
|
|
|
Kind: a.Kind,
|
|
|
|
MetaName: a.MetaName,
|
|
|
|
}
|
|
|
|
if sra.MetaName == "" && a.PkgName != nil {
|
|
|
|
sra.MetaName = *a.PkgName
|
|
|
|
}
|
|
|
|
sr.Associations = append(sr.Associations, sra)
|
2020-06-24 18:27:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
resID, err := influxdb.IDFromString(r.ID)
|
|
|
|
if err != nil {
|
|
|
|
return Stack{}, influxErr(influxdb.EInternal, err)
|
|
|
|
}
|
|
|
|
sr.ID = *resID
|
|
|
|
|
|
|
|
if sr.MetaName == "" && r.PkgName != nil {
|
|
|
|
sr.MetaName = *r.PkgName
|
|
|
|
}
|
|
|
|
newStack.Resources = append(newStack.Resources, sr)
|
|
|
|
}
|
2020-06-17 03:12:37 +00:00
|
|
|
|
|
|
|
id, err := influxdb.IDFromString(respStack.ID)
|
|
|
|
if err != nil {
|
|
|
|
return Stack{}, err
|
|
|
|
}
|
|
|
|
newStack.ID = *id
|
|
|
|
|
|
|
|
orgID, err := influxdb.IDFromString(respStack.OrgID)
|
|
|
|
if err != nil {
|
|
|
|
return Stack{}, err
|
|
|
|
}
|
|
|
|
newStack.OrgID = *orgID
|
|
|
|
|
|
|
|
return newStack, nil
|
|
|
|
}
|