2019-11-08 19:33:41 +00:00
|
|
|
package pkger
|
|
|
|
|
|
|
|
import (
|
2020-03-18 18:47:13 +00:00
|
|
|
"context"
|
2019-11-08 19:33:41 +00:00
|
|
|
"errors"
|
2020-03-18 18:47:13 +00:00
|
|
|
"fmt"
|
2019-12-23 22:31:56 +00:00
|
|
|
"regexp"
|
2019-11-08 19:33:41 +00:00
|
|
|
"sort"
|
2019-12-23 22:31:56 +00:00
|
|
|
"strings"
|
2019-11-08 19:33:41 +00:00
|
|
|
|
2020-04-03 17:39:20 +00:00
|
|
|
"github.com/influxdata/influxdb/v2"
|
|
|
|
ierrors "github.com/influxdata/influxdb/v2/kit/errors"
|
|
|
|
"github.com/influxdata/influxdb/v2/notification"
|
|
|
|
icheck "github.com/influxdata/influxdb/v2/notification/check"
|
|
|
|
"github.com/influxdata/influxdb/v2/notification/endpoint"
|
|
|
|
"github.com/influxdata/influxdb/v2/notification/rule"
|
|
|
|
"github.com/influxdata/influxdb/v2/pkger/internal/wordplay"
|
|
|
|
"github.com/influxdata/influxdb/v2/snowflake"
|
2019-11-08 19:33:41 +00:00
|
|
|
)
|
|
|
|
|
2020-03-20 03:26:32 +00:00
|
|
|
var idGenerator = snowflake.NewDefaultIDGenerator()
|
|
|
|
|
|
|
|
// NameGenerator generates a random name. Includes an optional fuzz option to
|
|
|
|
// further randomize the name.
|
|
|
|
type NameGenerator func() string
|
2020-03-18 18:47:13 +00:00
|
|
|
|
2019-11-08 19:33:41 +00:00
|
|
|
// ResourceToClone is a resource that will be cloned.
|
|
|
|
type ResourceToClone struct {
|
|
|
|
Kind Kind `json:"kind"`
|
2020-08-27 21:25:28 +00:00
|
|
|
ID influxdb.ID `json:"id,omitempty"`
|
2019-11-08 19:33:41 +00:00
|
|
|
Name string `json:"name"`
|
2020-06-24 18:27:03 +00:00
|
|
|
// note(jsteenb2): For time being we'll allow this internally, but not externally. A lot of
|
|
|
|
// issues to account for when exposing this to the outside world. Not something I'm keen
|
|
|
|
// to accommodate at this time.
|
|
|
|
MetaName string `json:"-"`
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// OK validates a resource clone is viable.
|
|
|
|
func (r ResourceToClone) OK() error {
|
|
|
|
if err := r.Kind.OK(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-27 21:25:28 +00:00
|
|
|
if r.ID == influxdb.ID(0) && len(r.Name) == 0 {
|
|
|
|
return errors.New("must provide an ID or name")
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-03-18 18:47:13 +00:00
|
|
|
var kindPriorities = map[Kind]int{
|
|
|
|
KindLabel: 1,
|
|
|
|
KindBucket: 2,
|
|
|
|
KindCheck: 3,
|
|
|
|
KindCheckDeadman: 4,
|
|
|
|
KindCheckThreshold: 5,
|
|
|
|
KindNotificationEndpoint: 6,
|
|
|
|
KindNotificationEndpointHTTP: 7,
|
|
|
|
KindNotificationEndpointPagerDuty: 8,
|
|
|
|
KindNotificationEndpointSlack: 9,
|
|
|
|
KindNotificationRule: 10,
|
|
|
|
KindTask: 11,
|
|
|
|
KindVariable: 12,
|
|
|
|
KindDashboard: 13,
|
|
|
|
KindTelegraf: 14,
|
|
|
|
}
|
|
|
|
|
|
|
|
type exportKey struct {
|
|
|
|
orgID influxdb.ID
|
2020-03-18 22:54:02 +00:00
|
|
|
id influxdb.ID
|
2020-03-18 18:47:13 +00:00
|
|
|
name string
|
|
|
|
kind Kind
|
|
|
|
}
|
|
|
|
|
2020-03-18 22:54:02 +00:00
|
|
|
func newExportKey(orgID, id influxdb.ID, k Kind, name string) exportKey {
|
2020-03-18 18:47:13 +00:00
|
|
|
return exportKey{
|
|
|
|
orgID: orgID,
|
2020-03-18 22:54:02 +00:00
|
|
|
id: id,
|
2020-03-18 18:47:13 +00:00
|
|
|
name: name,
|
|
|
|
kind: k,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 22:22:17 +00:00
|
|
|
type resourceExporter struct {
|
2020-03-20 03:26:32 +00:00
|
|
|
nameGen NameGenerator
|
|
|
|
|
2020-03-18 18:47:13 +00:00
|
|
|
bucketSVC influxdb.BucketService
|
|
|
|
checkSVC influxdb.CheckService
|
|
|
|
dashSVC influxdb.DashboardService
|
|
|
|
labelSVC influxdb.LabelService
|
|
|
|
endpointSVC influxdb.NotificationEndpointService
|
|
|
|
ruleSVC influxdb.NotificationRuleStore
|
|
|
|
taskSVC influxdb.TaskService
|
|
|
|
teleSVC influxdb.TelegrafConfigStore
|
|
|
|
varSVC influxdb.VariableService
|
|
|
|
|
2020-06-26 03:17:11 +00:00
|
|
|
mObjects map[exportKey]Object
|
|
|
|
mPkgNames map[string]bool
|
|
|
|
mStackResources map[exportKey]StackResource
|
2020-03-18 18:47:13 +00:00
|
|
|
}
|
|
|
|
|
2020-03-18 22:22:17 +00:00
|
|
|
func newResourceExporter(svc *Service) *resourceExporter {
|
|
|
|
return &resourceExporter{
|
2020-06-26 03:17:11 +00:00
|
|
|
nameGen: wordplay.GetRandomName,
|
|
|
|
bucketSVC: svc.bucketSVC,
|
|
|
|
checkSVC: svc.checkSVC,
|
|
|
|
dashSVC: svc.dashSVC,
|
|
|
|
labelSVC: svc.labelSVC,
|
|
|
|
endpointSVC: svc.endpointSVC,
|
|
|
|
ruleSVC: svc.ruleSVC,
|
|
|
|
taskSVC: svc.taskSVC,
|
|
|
|
teleSVC: svc.teleSVC,
|
|
|
|
varSVC: svc.varSVC,
|
|
|
|
mObjects: make(map[exportKey]Object),
|
|
|
|
mPkgNames: make(map[string]bool),
|
|
|
|
mStackResources: make(map[exportKey]StackResource),
|
2020-03-18 22:22:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ex *resourceExporter) Export(ctx context.Context, resourcesToClone []ResourceToClone, labelNames ...string) error {
|
2020-06-24 18:27:03 +00:00
|
|
|
mLabelIDsToMetaName := make(map[influxdb.ID]string)
|
|
|
|
for _, r := range resourcesToClone {
|
|
|
|
if !r.Kind.is(KindLabel) || r.MetaName == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
mLabelIDsToMetaName[r.ID] = r.MetaName
|
|
|
|
}
|
|
|
|
|
|
|
|
cloneAssFn, err := ex.resourceCloneAssociationsGen(ctx, mLabelIDsToMetaName, labelNames...)
|
2020-03-18 18:47:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
resourcesToClone = uniqResourcesToClone(resourcesToClone)
|
|
|
|
// sorting this in priority order guarantees that the dependencies/associations
|
|
|
|
// for a resource are handled prior to the resource being processed.
|
|
|
|
// i.e. if a bucket depends on a label, then labels need to be run first
|
|
|
|
// to guarantee they are available before a bucket is exported.
|
|
|
|
sort.Slice(resourcesToClone, func(i, j int) bool {
|
2020-08-27 21:25:28 +00:00
|
|
|
// todo: how did this compile, i just added Name to the type.
|
2020-03-18 18:47:13 +00:00
|
|
|
iName, jName := resourcesToClone[i].Name, resourcesToClone[j].Name
|
|
|
|
iKind, jKind := resourcesToClone[i].Kind, resourcesToClone[j].Kind
|
|
|
|
|
|
|
|
if iKind.is(jKind) {
|
|
|
|
return iName < jName
|
|
|
|
}
|
|
|
|
return kindPriorities[iKind] < kindPriorities[jKind]
|
|
|
|
})
|
|
|
|
|
|
|
|
for _, r := range resourcesToClone {
|
|
|
|
err := ex.resourceCloneToKind(ctx, r, cloneAssFn)
|
|
|
|
if err != nil {
|
|
|
|
return internalErr(fmt.Errorf("failed to clone resource: resource_id=%s resource_kind=%s err=%q", r.ID, r.Kind, err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-03-18 22:22:17 +00:00
|
|
|
func (ex *resourceExporter) Objects() []Object {
|
2020-03-18 18:47:13 +00:00
|
|
|
objects := make([]Object, 0, len(ex.mObjects))
|
|
|
|
for _, obj := range ex.mObjects {
|
|
|
|
objects = append(objects, obj)
|
|
|
|
}
|
|
|
|
|
2020-06-01 21:58:36 +00:00
|
|
|
return sortObjects(objects)
|
2020-03-18 18:47:13 +00:00
|
|
|
}
|
|
|
|
|
2020-06-26 03:17:11 +00:00
|
|
|
func (ex *resourceExporter) StackResources() []StackResource {
|
|
|
|
resources := make([]StackResource, 0, len(ex.mStackResources))
|
|
|
|
for _, res := range ex.mStackResources {
|
|
|
|
resources = append(resources, res)
|
|
|
|
}
|
|
|
|
return resources
|
|
|
|
}
|
|
|
|
|
2020-08-27 21:25:28 +00:00
|
|
|
// we only need an id when we have resources that are not unique by name via the
|
|
|
|
// metastore. resoureces that are unique by name will be provided a default stamp
|
|
|
|
// making looksup unique since each resource will be unique by name.
|
|
|
|
const uniqByNameResID = influxdb.ID(0)
|
2020-03-18 22:54:02 +00:00
|
|
|
|
2020-06-24 18:27:03 +00:00
|
|
|
type cloneAssociationsFn func(context.Context, ResourceToClone) (associations []ObjectAssociation, skipResource bool, err error)
|
2020-03-18 18:47:13 +00:00
|
|
|
|
2020-03-18 22:22:17 +00:00
|
|
|
func (ex *resourceExporter) resourceCloneToKind(ctx context.Context, r ResourceToClone, cFn cloneAssociationsFn) (e error) {
|
2020-03-18 18:47:13 +00:00
|
|
|
defer func() {
|
|
|
|
if e != nil {
|
|
|
|
e = ierrors.Wrap(e, "cloning resource")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
ass, skipResource, err := cFn(ctx, r)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if skipResource {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-03-18 22:54:02 +00:00
|
|
|
mapResource := func(orgID, uniqResID influxdb.ID, k Kind, object Object) {
|
2020-03-20 03:26:32 +00:00
|
|
|
// overwrite the default metadata.name field with export generated one here
|
2020-06-24 18:27:03 +00:00
|
|
|
metaName := r.MetaName
|
|
|
|
if r.MetaName == "" {
|
|
|
|
metaName = ex.uniqName()
|
2020-03-18 18:47:13 +00:00
|
|
|
}
|
2020-06-26 03:17:11 +00:00
|
|
|
|
|
|
|
stackResource := StackResource{
|
|
|
|
APIVersion: APIVersion,
|
|
|
|
ID: r.ID,
|
|
|
|
MetaName: metaName,
|
|
|
|
Kind: r.Kind,
|
|
|
|
}
|
|
|
|
for _, a := range ass {
|
|
|
|
stackResource.Associations = append(stackResource.Associations, StackResourceAssociation(a))
|
|
|
|
}
|
|
|
|
|
2020-06-24 18:27:03 +00:00
|
|
|
object.SetMetadataName(metaName)
|
|
|
|
object.AddAssociations(ass...)
|
2020-03-18 22:54:02 +00:00
|
|
|
key := newExportKey(orgID, uniqResID, k, object.Spec.stringShort(fieldName))
|
2020-03-18 18:47:13 +00:00
|
|
|
ex.mObjects[key] = object
|
2020-06-26 03:17:11 +00:00
|
|
|
ex.mStackResources[key] = stackResource
|
2020-03-18 18:47:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case r.Kind.is(KindBucket):
|
2020-08-27 22:31:25 +00:00
|
|
|
filter := influxdb.BucketFilter{}
|
|
|
|
if r.ID != influxdb.ID(0) {
|
|
|
|
filter.ID = &r.ID
|
|
|
|
}
|
|
|
|
if len(r.Name) > 0 {
|
|
|
|
filter.Name = &r.Name
|
|
|
|
}
|
|
|
|
|
|
|
|
bkts, n, err := ex.bucketSVC.FindBuckets(ctx, filter)
|
2020-03-18 18:47:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-27 22:31:25 +00:00
|
|
|
if n < 1 {
|
|
|
|
return errors.New("no buckets found")
|
|
|
|
}
|
2020-08-27 22:55:23 +00:00
|
|
|
|
2020-08-27 22:31:25 +00:00
|
|
|
for _, bkt := range bkts {
|
|
|
|
mapResource(bkt.OrgID, uniqByNameResID, KindBucket, BucketToObject(r.Name, *bkt))
|
|
|
|
}
|
2020-08-27 22:55:23 +00:00
|
|
|
case r.Kind.is(KindCheck), r.Kind.is(KindCheckDeadman), r.Kind.is(KindCheckThreshold):
|
|
|
|
filter := influxdb.CheckFilter{}
|
|
|
|
if r.ID != influxdb.ID(0) {
|
|
|
|
filter.ID = &r.ID
|
|
|
|
}
|
|
|
|
if len(r.Name) > 0 {
|
|
|
|
filter.Name = &r.Name
|
|
|
|
}
|
|
|
|
|
|
|
|
chs, n, err := ex.checkSVC.FindChecks(ctx, filter)
|
2020-03-18 18:47:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-27 22:55:23 +00:00
|
|
|
if n < 1 {
|
|
|
|
return errors.New("no checks found")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, ch := range chs {
|
|
|
|
mapResource(ch.GetOrgID(), uniqByNameResID, KindCheck, CheckToObject(r.Name, ch))
|
|
|
|
}
|
2020-03-18 18:47:13 +00:00
|
|
|
case r.Kind.is(KindDashboard):
|
2020-08-27 22:10:13 +00:00
|
|
|
var (
|
|
|
|
hasID bool
|
|
|
|
filter = influxdb.DashboardFilter{}
|
|
|
|
)
|
2020-08-27 21:25:28 +00:00
|
|
|
if r.ID != influxdb.ID(0) {
|
2020-08-27 22:10:13 +00:00
|
|
|
hasID = true
|
2020-08-27 21:25:28 +00:00
|
|
|
filter.IDs = []*influxdb.ID{&r.ID}
|
|
|
|
}
|
2020-08-27 22:55:23 +00:00
|
|
|
|
2020-08-27 22:31:25 +00:00
|
|
|
dashs, n, err := ex.dashSVC.FindDashboards(ctx, filter, influxdb.DefaultDashboardFindOptions)
|
2020-03-18 18:47:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-27 22:31:25 +00:00
|
|
|
if n < 1 {
|
2020-08-27 21:25:28 +00:00
|
|
|
return errors.New("no dashboards found")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, dash := range dashs {
|
2020-08-27 22:10:13 +00:00
|
|
|
if (len(r.Name) > 0 && dash.Name != r.Name) || (hasID && dash.ID != r.ID) {
|
|
|
|
continue
|
|
|
|
}
|
2020-08-27 21:25:28 +00:00
|
|
|
for _, cell := range dash.Cells {
|
|
|
|
v, err := ex.dashSVC.GetDashboardCellView(ctx, dash.ID, cell.ID)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
cell.View = v
|
|
|
|
}
|
|
|
|
|
|
|
|
mapResource(dash.OrganizationID, dash.ID, KindDashboard, DashboardToObject(r.Name, *dash))
|
|
|
|
}
|
2020-03-18 18:47:13 +00:00
|
|
|
case r.Kind.is(KindLabel):
|
2020-08-27 23:01:23 +00:00
|
|
|
switch {
|
|
|
|
case r.ID != influxdb.ID(0):
|
|
|
|
l, err := ex.labelSVC.FindLabelByID(ctx, r.ID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
mapResource(l.OrgID, uniqByNameResID, KindLabel, LabelToObject(r.Name, *l))
|
|
|
|
case len(r.Name) > 0:
|
|
|
|
labels, err := ex.labelSVC.FindLabels(ctx, influxdb.LabelFilter{Name: r.Name})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, l := range labels {
|
|
|
|
mapResource(l.OrgID, uniqByNameResID, KindLabel, LabelToObject(r.Name, *l))
|
|
|
|
}
|
2020-03-18 18:47:13 +00:00
|
|
|
}
|
|
|
|
case r.Kind.is(KindNotificationEndpoint),
|
|
|
|
r.Kind.is(KindNotificationEndpointHTTP),
|
|
|
|
r.Kind.is(KindNotificationEndpointPagerDuty),
|
|
|
|
r.Kind.is(KindNotificationEndpointSlack):
|
2020-08-27 23:19:55 +00:00
|
|
|
var (
|
|
|
|
hasID bool
|
|
|
|
filter = influxdb.NotificationEndpointFilter{}
|
|
|
|
)
|
|
|
|
if r.ID != influxdb.ID(0) {
|
|
|
|
filter.ID = &r.ID
|
|
|
|
}
|
|
|
|
|
|
|
|
endpoints, n, err := ex.endpointSVC.FindNotificationEndpoints(ctx, filter)
|
2020-03-18 18:47:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-27 23:19:55 +00:00
|
|
|
if n < 1 {
|
|
|
|
return errors.New("no notification endpoints found")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, e := range endpoints {
|
|
|
|
if (len(r.Name) > 0 && e.GetName() != r.Name) || (hasID && e.GetID() != r.ID) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
mapResource(e.GetOrgID(), uniqByNameResID, KindNotificationEndpoint, NotificationEndpointToObject(r.Name, e))
|
|
|
|
}
|
2020-03-18 18:47:13 +00:00
|
|
|
case r.Kind.is(KindNotificationRule):
|
2020-08-27 23:29:25 +00:00
|
|
|
var rules []influxdb.NotificationRule
|
|
|
|
|
|
|
|
if r.ID != influxdb.ID(0) {
|
|
|
|
r, err := ex.ruleSVC.FindNotificationRuleByID(ctx, r.ID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
rules = append(rules, r)
|
2020-03-18 18:47:13 +00:00
|
|
|
}
|
|
|
|
|
2020-08-27 23:29:25 +00:00
|
|
|
if len(r.Name) != 0 {
|
|
|
|
allRules, n, err := ex.ruleSVC.FindNotificationRules(ctx, influxdb.NotificationRuleFilter{})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if n < 1 {
|
|
|
|
return errors.New("no notification rules found")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, rule := range allRules {
|
|
|
|
if rule.GetName() != r.Name {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
rules = append(rules, rule)
|
|
|
|
}
|
2020-03-18 18:47:13 +00:00
|
|
|
}
|
|
|
|
|
2020-08-27 23:29:25 +00:00
|
|
|
for _, rule := range rules {
|
|
|
|
ruleEndpoint, err := ex.endpointSVC.FindNotificationEndpointByID(ctx, rule.GetEndpointID())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
endpointKey := newExportKey(ruleEndpoint.GetOrgID(), uniqByNameResID, KindNotificationEndpoint, ruleEndpoint.GetName())
|
|
|
|
object, ok := ex.mObjects[endpointKey]
|
|
|
|
if !ok {
|
|
|
|
mapResource(ruleEndpoint.GetOrgID(), uniqByNameResID, KindNotificationEndpoint, NotificationEndpointToObject("", ruleEndpoint))
|
|
|
|
object = ex.mObjects[endpointKey]
|
|
|
|
}
|
|
|
|
endpointObjectName := object.Name()
|
|
|
|
|
|
|
|
mapResource(rule.GetOrgID(), rule.GetID(), KindNotificationRule, NotificationRuleToObject(r.Name, endpointObjectName, rule))
|
|
|
|
}
|
2020-03-18 18:47:13 +00:00
|
|
|
case r.Kind.is(KindTask):
|
|
|
|
t, err := ex.taskSVC.FindTaskByID(ctx, r.ID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
feat(pkger): add stateful management for tasks
notes on this commit. This commit was grueling ;-(. The task API is not a friendly
API to consume. There are a lot of non obvious things going on and almost every
one of them tripped me up. Things of note:
* the http.TaskService does not satisfy the influxdb.TaskService,
making it impossible to use as a dependency if tasks service gets
split out
* the APIs for create and update do not share common types. For example:
creating a task takes every field as a string, but in the update it is
taken as a options.Duration type. A step further and you'll notice that
create does not need an option to be provided, but the update does. Its
jarring trying to understand the indirection here. I struggled mightily
trying to make sense of it all with the indirection and differing types.
Made for a very difficult task (no pun intended) when it should have been
trivial. Opportunity here to fix these up and make this API more uniform
and remove unneccesary complexity like the options type.
* Nested IDs that get marshaled, are no bueno when you want to marshal a task
that does not have an ID in it, for either user/org/or self IDs. Its a challenge
just to do that.
* Lots of logs in the kv.Task portion where we hit errors and log and others where
we return. It isn't clear what is happening. The kv implementation is also very
procedural, and I found myself bouncing around like a ping pong ball trying to
make heads or tails of it.
* There is auth buried deep inside the kv.Task implementation that kept throwing me
off b/c it kept throwing errors, instead of warns. I assume, not sure if I'm
correct on this, but that the stuff being logged is determined inconsequential
to the task working. I had lots of errors from the auth buried in there, and hadn't
a clue what to make of it....
leaving these notes here as a look back at why working with tasks is so
difficult. This API can improve dramatically. I spent 5x the time trying
to figure out how to use the task API, in procedural calls, than I did
writing the business logic to consume it.... that's a scary realization ;-(
references: #17434
2020-04-21 02:59:56 +00:00
|
|
|
mapResource(t.OrganizationID, t.ID, KindTask, TaskToObject(r.Name, *t))
|
2020-03-18 18:47:13 +00:00
|
|
|
case r.Kind.is(KindTelegraf):
|
|
|
|
t, err := ex.teleSVC.FindTelegrafConfigByID(ctx, r.ID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-04-21 22:00:29 +00:00
|
|
|
mapResource(t.OrgID, t.ID, KindTelegraf, TelegrafToObject(r.Name, *t))
|
2020-03-18 18:47:13 +00:00
|
|
|
case r.Kind.is(KindVariable):
|
|
|
|
v, err := ex.varSVC.FindVariableByID(ctx, r.ID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-04-03 00:44:27 +00:00
|
|
|
mapResource(v.OrganizationID, uniqByNameResID, KindVariable, VariableToObject(r.Name, *v))
|
2020-03-18 18:47:13 +00:00
|
|
|
default:
|
|
|
|
return errors.New("unsupported kind provided: " + string(r.Kind))
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-24 18:27:03 +00:00
|
|
|
func (ex *resourceExporter) resourceCloneAssociationsGen(ctx context.Context, labelIDsToMetaName map[influxdb.ID]string, labelNames ...string) (cloneAssociationsFn, error) {
|
2020-03-18 18:47:13 +00:00
|
|
|
mLabelNames := make(map[string]bool)
|
|
|
|
for _, labelName := range labelNames {
|
|
|
|
mLabelNames[labelName] = true
|
|
|
|
}
|
|
|
|
|
|
|
|
mLabelIDs, err := getLabelIDMap(ctx, ex.labelSVC, labelNames)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-06-24 18:27:03 +00:00
|
|
|
cloneFn := func(ctx context.Context, r ResourceToClone) ([]ObjectAssociation, bool, error) {
|
2020-03-18 18:47:13 +00:00
|
|
|
if r.Kind.is(KindUnknown) {
|
|
|
|
return nil, true, nil
|
|
|
|
}
|
|
|
|
if r.Kind.is(KindLabel) {
|
|
|
|
// check here verifies the label maps to an id of a valid label name
|
|
|
|
shouldSkip := len(mLabelIDs) > 0 && !mLabelIDs[r.ID]
|
|
|
|
return nil, shouldSkip, nil
|
|
|
|
}
|
|
|
|
|
2020-08-27 21:25:28 +00:00
|
|
|
if len(r.Name) > 0 {
|
|
|
|
return nil, false, nil
|
|
|
|
}
|
|
|
|
|
2020-03-18 18:47:13 +00:00
|
|
|
labels, err := ex.labelSVC.FindResourceLabels(ctx, influxdb.LabelMappingFilter{
|
|
|
|
ResourceID: r.ID,
|
|
|
|
ResourceType: r.Kind.ResourceType(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, ierrors.Wrap(err, "finding resource labels")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(mLabelNames) > 0 {
|
|
|
|
shouldSkip := true
|
|
|
|
for _, l := range labels {
|
|
|
|
if _, ok := mLabelNames[l.Name]; ok {
|
|
|
|
shouldSkip = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if shouldSkip {
|
|
|
|
return nil, true, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-24 18:27:03 +00:00
|
|
|
var associations []ObjectAssociation
|
2020-03-18 18:47:13 +00:00
|
|
|
for _, l := range labels {
|
|
|
|
if len(mLabelNames) > 0 {
|
|
|
|
if _, ok := mLabelNames[l.Name]; !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-01 23:44:17 +00:00
|
|
|
labelObject := LabelToObject("", *l)
|
2020-06-24 18:27:03 +00:00
|
|
|
metaName := labelIDsToMetaName[l.ID]
|
|
|
|
if metaName == "" {
|
|
|
|
metaName = ex.uniqName()
|
|
|
|
}
|
|
|
|
labelObject.Metadata[fieldName] = metaName
|
2020-03-18 18:47:13 +00:00
|
|
|
|
2020-08-27 21:25:28 +00:00
|
|
|
k := newExportKey(l.OrgID, uniqByNameResID, KindLabel, l.Name)
|
2020-03-18 18:47:13 +00:00
|
|
|
existing, ok := ex.mObjects[k]
|
|
|
|
if ok {
|
2020-06-24 18:27:03 +00:00
|
|
|
associations = append(associations, ObjectAssociation{
|
|
|
|
Kind: KindLabel,
|
|
|
|
MetaName: existing.Name(),
|
2020-03-18 18:47:13 +00:00
|
|
|
})
|
|
|
|
continue
|
|
|
|
}
|
2020-06-24 18:27:03 +00:00
|
|
|
associations = append(associations, ObjectAssociation{
|
|
|
|
Kind: KindLabel,
|
|
|
|
MetaName: labelObject.Name(),
|
2020-03-18 18:47:13 +00:00
|
|
|
})
|
|
|
|
ex.mObjects[k] = labelObject
|
|
|
|
}
|
2020-06-24 18:27:03 +00:00
|
|
|
sort.Slice(associations, func(i, j int) bool {
|
|
|
|
return associations[i].MetaName < associations[j].MetaName
|
|
|
|
})
|
2020-03-18 18:47:13 +00:00
|
|
|
return associations, false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return cloneFn, nil
|
|
|
|
}
|
|
|
|
|
2020-03-20 03:26:32 +00:00
|
|
|
func (ex *resourceExporter) uniqName() string {
|
2020-07-07 22:07:11 +00:00
|
|
|
return uniqMetaName(ex.nameGen, idGenerator, ex.mPkgNames)
|
2020-06-26 20:58:22 +00:00
|
|
|
}
|
|
|
|
|
2020-07-07 22:07:11 +00:00
|
|
|
func uniqMetaName(nameGen NameGenerator, idGen influxdb.IDGenerator, existingNames map[string]bool) string {
|
|
|
|
uuid := strings.ToLower(idGen.ID().String())
|
2020-06-26 20:58:22 +00:00
|
|
|
name := uuid
|
2020-03-20 03:26:32 +00:00
|
|
|
for i := 1; i < 250; i++ {
|
2020-06-26 20:58:22 +00:00
|
|
|
name = fmt.Sprintf("%s-%s", nameGen(), uuid[10:])
|
|
|
|
if !existingNames[name] {
|
2020-07-07 22:07:11 +00:00
|
|
|
break
|
2020-03-20 03:26:32 +00:00
|
|
|
}
|
|
|
|
}
|
2020-06-26 20:58:22 +00:00
|
|
|
return name
|
2020-03-20 03:26:32 +00:00
|
|
|
}
|
|
|
|
|
2019-11-21 00:38:12 +00:00
|
|
|
func uniqResourcesToClone(resources []ResourceToClone) []ResourceToClone {
|
|
|
|
type key struct {
|
|
|
|
kind Kind
|
|
|
|
id influxdb.ID
|
|
|
|
}
|
|
|
|
m := make(map[key]ResourceToClone)
|
|
|
|
|
|
|
|
for i := range resources {
|
|
|
|
r := resources[i]
|
|
|
|
rKey := key{kind: r.Kind, id: r.ID}
|
2020-06-24 18:27:03 +00:00
|
|
|
|
2019-11-21 00:38:12 +00:00
|
|
|
kr, ok := m[rKey]
|
|
|
|
switch {
|
2020-06-24 18:27:03 +00:00
|
|
|
case ok && kr.Name == r.Name && kr.MetaName == r.MetaName:
|
|
|
|
case ok && kr.MetaName != "" && r.MetaName == "":
|
|
|
|
case ok && kr.MetaName == "" && kr.Name != "" && r.Name == "":
|
2019-11-21 00:38:12 +00:00
|
|
|
default:
|
|
|
|
m[rKey] = r
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out := make([]ResourceToClone, 0, len(resources))
|
|
|
|
for _, r := range m {
|
|
|
|
out = append(out, r)
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2020-04-01 23:44:17 +00:00
|
|
|
// BucketToObject converts a influxdb.Bucket into an Object.
|
|
|
|
func BucketToObject(name string, bkt influxdb.Bucket) Object {
|
2019-11-08 19:33:41 +00:00
|
|
|
if name == "" {
|
|
|
|
name = bkt.Name
|
|
|
|
}
|
2020-03-18 18:47:13 +00:00
|
|
|
|
|
|
|
o := newObject(KindBucket, name)
|
|
|
|
assignNonZeroStrings(o.Spec, map[string]string{fieldDescription: bkt.Description})
|
2019-11-21 00:38:12 +00:00
|
|
|
if bkt.RetentionPeriod != 0 {
|
2020-03-18 18:47:13 +00:00
|
|
|
o.Spec[fieldBucketRetentionRules] = retentionRules{newRetentionRule(bkt.RetentionPeriod)}
|
2019-11-21 00:38:12 +00:00
|
|
|
}
|
2020-03-18 18:47:13 +00:00
|
|
|
return o
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
|
|
|
|
2020-04-02 22:28:11 +00:00
|
|
|
func CheckToObject(name string, ch influxdb.Check) Object {
|
2019-12-19 01:03:19 +00:00
|
|
|
if name == "" {
|
|
|
|
name = ch.GetName()
|
|
|
|
}
|
2020-04-02 22:28:11 +00:00
|
|
|
o := newObject(KindCheck, name)
|
2020-03-18 18:47:13 +00:00
|
|
|
assignNonZeroStrings(o.Spec, map[string]string{
|
|
|
|
fieldDescription: ch.GetDescription(),
|
|
|
|
fieldStatus: influxdb.TaskStatusActive,
|
|
|
|
})
|
2019-12-19 01:03:19 +00:00
|
|
|
|
|
|
|
assignBase := func(base icheck.Base) {
|
2020-03-18 18:47:13 +00:00
|
|
|
o.Spec[fieldQuery] = strings.TrimSpace(base.Query.Text)
|
|
|
|
o.Spec[fieldCheckStatusMessageTemplate] = base.StatusMessageTemplate
|
|
|
|
assignNonZeroFluxDurs(o.Spec, map[string]*notification.Duration{
|
2019-12-20 20:51:27 +00:00
|
|
|
fieldEvery: base.Every,
|
|
|
|
fieldOffset: base.Offset,
|
|
|
|
})
|
|
|
|
|
2019-12-19 01:03:19 +00:00
|
|
|
var tags []Resource
|
|
|
|
for _, t := range base.Tags {
|
|
|
|
if t.Valid() != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
tags = append(tags, Resource{
|
|
|
|
fieldKey: t.Key,
|
|
|
|
fieldValue: t.Value,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if len(tags) > 0 {
|
2020-03-18 18:47:13 +00:00
|
|
|
o.Spec[fieldCheckTags] = tags
|
2019-12-19 01:03:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch cT := ch.(type) {
|
|
|
|
case *icheck.Deadman:
|
2020-03-18 18:47:13 +00:00
|
|
|
o.Kind = KindCheckDeadman
|
2019-12-19 01:03:19 +00:00
|
|
|
assignBase(cT.Base)
|
2020-03-18 18:47:13 +00:00
|
|
|
assignNonZeroFluxDurs(o.Spec, map[string]*notification.Duration{
|
2019-12-20 20:51:27 +00:00
|
|
|
fieldCheckTimeSince: cT.TimeSince,
|
|
|
|
fieldCheckStaleTime: cT.StaleTime,
|
|
|
|
})
|
2020-03-18 18:47:13 +00:00
|
|
|
o.Spec[fieldLevel] = cT.Level.String()
|
|
|
|
assignNonZeroBools(o.Spec, map[string]bool{fieldCheckReportZero: cT.ReportZero})
|
2019-12-19 01:03:19 +00:00
|
|
|
case *icheck.Threshold:
|
2020-03-18 18:47:13 +00:00
|
|
|
o.Kind = KindCheckThreshold
|
2019-12-19 01:03:19 +00:00
|
|
|
assignBase(cT.Base)
|
|
|
|
var thresholds []Resource
|
|
|
|
for _, th := range cT.Thresholds {
|
|
|
|
thresholds = append(thresholds, convertThreshold(th))
|
|
|
|
}
|
2020-03-18 18:47:13 +00:00
|
|
|
o.Spec[fieldCheckThresholds] = thresholds
|
2019-12-19 01:03:19 +00:00
|
|
|
}
|
2020-03-18 18:47:13 +00:00
|
|
|
return o
|
2019-12-19 01:03:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func convertThreshold(th icheck.ThresholdConfig) Resource {
|
2019-12-19 19:56:03 +00:00
|
|
|
r := Resource{fieldLevel: th.GetLevel().String()}
|
2019-12-19 01:03:19 +00:00
|
|
|
|
2019-12-20 20:51:27 +00:00
|
|
|
assignLesser := func(threshType thresholdType, allValues bool, val float64) {
|
|
|
|
r[fieldType] = string(threshType)
|
|
|
|
assignNonZeroBools(r, map[string]bool{fieldCheckAllValues: allValues})
|
|
|
|
r[fieldValue] = val
|
|
|
|
}
|
|
|
|
|
2019-12-19 01:03:19 +00:00
|
|
|
switch realType := th.(type) {
|
|
|
|
case icheck.Lesser:
|
2019-12-20 20:51:27 +00:00
|
|
|
assignLesser(thresholdTypeLesser, realType.AllValues, realType.Value)
|
|
|
|
case *icheck.Lesser:
|
|
|
|
assignLesser(thresholdTypeLesser, realType.AllValues, realType.Value)
|
2019-12-19 01:03:19 +00:00
|
|
|
case icheck.Greater:
|
2019-12-20 20:51:27 +00:00
|
|
|
assignLesser(thresholdTypeGreater, realType.AllValues, realType.Value)
|
|
|
|
case *icheck.Greater:
|
|
|
|
assignLesser(thresholdTypeGreater, realType.AllValues, realType.Value)
|
2019-12-19 01:03:19 +00:00
|
|
|
case icheck.Range:
|
|
|
|
assignRangeThreshold(r, realType)
|
|
|
|
case *icheck.Range:
|
|
|
|
assignRangeThreshold(r, *realType)
|
|
|
|
}
|
|
|
|
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func assignRangeThreshold(r Resource, rangeThreshold icheck.Range) {
|
|
|
|
thType := thresholdTypeOutsideRange
|
|
|
|
if rangeThreshold.Within {
|
|
|
|
thType = thresholdTypeInsideRange
|
|
|
|
}
|
|
|
|
r[fieldType] = string(thType)
|
|
|
|
assignNonZeroBools(r, map[string]bool{fieldCheckAllValues: rangeThreshold.AllValues})
|
|
|
|
r[fieldMax] = rangeThreshold.Max
|
|
|
|
r[fieldMin] = rangeThreshold.Min
|
|
|
|
}
|
2020-01-13 19:13:37 +00:00
|
|
|
|
2019-12-06 17:13:06 +00:00
|
|
|
func convertCellView(cell influxdb.Cell) chart {
|
|
|
|
var name string
|
|
|
|
if cell.View != nil {
|
|
|
|
name = cell.View.Name
|
|
|
|
}
|
2019-11-08 19:33:41 +00:00
|
|
|
ch := chart{
|
2019-12-06 17:13:06 +00:00
|
|
|
Name: name,
|
|
|
|
Height: int(cell.H),
|
|
|
|
Width: int(cell.W),
|
|
|
|
XPos: int(cell.X),
|
|
|
|
YPos: int(cell.Y),
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
setCommon := func(k chartKind, iColors []influxdb.ViewColor, dec influxdb.DecimalPlaces, iQueries []influxdb.DashboardQuery) {
|
|
|
|
ch.Kind = k
|
|
|
|
ch.Colors = convertColors(iColors)
|
|
|
|
ch.DecimalPlaces = int(dec.Digits)
|
|
|
|
ch.EnforceDecimals = dec.IsEnforced
|
|
|
|
ch.Queries = convertQueries(iQueries)
|
|
|
|
}
|
|
|
|
|
|
|
|
setNoteFixes := func(note string, noteOnEmpty bool, prefix, suffix string) {
|
|
|
|
ch.Note = note
|
|
|
|
ch.NoteOnEmpty = noteOnEmpty
|
|
|
|
ch.Prefix = prefix
|
|
|
|
ch.Suffix = suffix
|
|
|
|
}
|
|
|
|
|
|
|
|
setLegend := func(l influxdb.Legend) {
|
|
|
|
ch.Legend.Orientation = l.Orientation
|
|
|
|
ch.Legend.Type = l.Type
|
|
|
|
}
|
|
|
|
|
2019-12-06 17:13:06 +00:00
|
|
|
props := cell.View.Properties
|
2019-11-08 19:33:41 +00:00
|
|
|
switch p := props.(type) {
|
|
|
|
case influxdb.GaugeViewProperties:
|
|
|
|
setCommon(chartKindGauge, p.ViewColors, p.DecimalPlaces, p.Queries)
|
|
|
|
setNoteFixes(p.Note, p.ShowNoteWhenEmpty, p.Prefix, p.Suffix)
|
2020-01-08 17:00:02 +00:00
|
|
|
ch.TickPrefix = p.TickPrefix
|
|
|
|
ch.TickSuffix = p.TickSuffix
|
2019-11-15 01:05:21 +00:00
|
|
|
case influxdb.HeatmapViewProperties:
|
|
|
|
ch.Kind = chartKindHeatMap
|
|
|
|
ch.Queries = convertQueries(p.Queries)
|
|
|
|
ch.Colors = stringsToColors(p.ViewColors)
|
|
|
|
ch.XCol = p.XColumn
|
|
|
|
ch.YCol = p.YColumn
|
|
|
|
ch.Axes = []axis{
|
|
|
|
{Label: p.XAxisLabel, Prefix: p.XPrefix, Suffix: p.XSuffix, Name: "x", Domain: p.XDomain},
|
|
|
|
{Label: p.YAxisLabel, Prefix: p.YPrefix, Suffix: p.YSuffix, Name: "y", Domain: p.YDomain},
|
|
|
|
}
|
|
|
|
ch.Note = p.Note
|
|
|
|
ch.NoteOnEmpty = p.ShowNoteWhenEmpty
|
|
|
|
ch.BinSize = int(p.BinSize)
|
2019-11-16 20:14:46 +00:00
|
|
|
case influxdb.HistogramViewProperties:
|
|
|
|
ch.Kind = chartKindHistogram
|
|
|
|
ch.Queries = convertQueries(p.Queries)
|
|
|
|
ch.Colors = convertColors(p.ViewColors)
|
2020-06-10 12:57:09 +00:00
|
|
|
ch.FillColumns = p.FillColumns
|
2019-11-16 20:14:46 +00:00
|
|
|
ch.XCol = p.XColumn
|
|
|
|
ch.Axes = []axis{{Label: p.XAxisLabel, Name: "x", Domain: p.XDomain}}
|
|
|
|
ch.Note = p.Note
|
|
|
|
ch.NoteOnEmpty = p.ShowNoteWhenEmpty
|
|
|
|
ch.BinCount = p.BinCount
|
|
|
|
ch.Position = p.Position
|
2019-11-15 01:05:21 +00:00
|
|
|
case influxdb.MarkdownViewProperties:
|
|
|
|
ch.Kind = chartKindMarkdown
|
|
|
|
ch.Note = p.Note
|
2019-11-08 19:33:41 +00:00
|
|
|
case influxdb.LinePlusSingleStatProperties:
|
|
|
|
setCommon(chartKindSingleStatPlusLine, p.ViewColors, p.DecimalPlaces, p.Queries)
|
|
|
|
setNoteFixes(p.Note, p.ShowNoteWhenEmpty, p.Prefix, p.Suffix)
|
|
|
|
setLegend(p.Legend)
|
|
|
|
ch.Axes = convertAxes(p.Axes)
|
|
|
|
ch.Shade = p.ShadeBelow
|
2020-06-17 14:16:46 +00:00
|
|
|
ch.HoverDimension = p.HoverDimension
|
2019-11-08 19:33:41 +00:00
|
|
|
ch.XCol = p.XColumn
|
|
|
|
ch.YCol = p.YColumn
|
2019-12-03 22:59:07 +00:00
|
|
|
ch.Position = p.Position
|
2019-11-08 19:33:41 +00:00
|
|
|
case influxdb.SingleStatViewProperties:
|
|
|
|
setCommon(chartKindSingleStat, p.ViewColors, p.DecimalPlaces, p.Queries)
|
|
|
|
setNoteFixes(p.Note, p.ShowNoteWhenEmpty, p.Prefix, p.Suffix)
|
2020-01-16 20:55:30 +00:00
|
|
|
ch.TickPrefix = p.TickPrefix
|
|
|
|
ch.TickSuffix = p.TickSuffix
|
2020-08-04 23:24:58 +00:00
|
|
|
case influxdb.MosaicViewProperties:
|
|
|
|
ch.Kind = chartKindMosaic
|
|
|
|
ch.Queries = convertQueries(p.Queries)
|
|
|
|
ch.Colors = stringsToColors(p.ViewColors)
|
|
|
|
ch.XCol = p.XColumn
|
|
|
|
ch.YSeriesColumns = p.YSeriesColumns
|
|
|
|
ch.Axes = []axis{
|
|
|
|
{Label: p.XAxisLabel, Prefix: p.XPrefix, Suffix: p.XSuffix, Name: "x", Domain: p.XDomain},
|
|
|
|
{Label: p.YAxisLabel, Prefix: p.YPrefix, Suffix: p.YSuffix, Name: "y", Domain: p.YDomain},
|
|
|
|
}
|
|
|
|
ch.Note = p.Note
|
|
|
|
ch.NoteOnEmpty = p.ShowNoteWhenEmpty
|
2019-11-15 01:05:21 +00:00
|
|
|
case influxdb.ScatterViewProperties:
|
|
|
|
ch.Kind = chartKindScatter
|
|
|
|
ch.Queries = convertQueries(p.Queries)
|
|
|
|
ch.Colors = stringsToColors(p.ViewColors)
|
|
|
|
ch.XCol = p.XColumn
|
|
|
|
ch.YCol = p.YColumn
|
|
|
|
ch.Axes = []axis{
|
|
|
|
{Label: p.XAxisLabel, Prefix: p.XPrefix, Suffix: p.XSuffix, Name: "x", Domain: p.XDomain},
|
|
|
|
{Label: p.YAxisLabel, Prefix: p.YPrefix, Suffix: p.YSuffix, Name: "y", Domain: p.YDomain},
|
|
|
|
}
|
2019-11-13 21:30:52 +00:00
|
|
|
ch.Note = p.Note
|
2019-11-15 01:05:21 +00:00
|
|
|
ch.NoteOnEmpty = p.ShowNoteWhenEmpty
|
2020-03-04 21:55:15 +00:00
|
|
|
case influxdb.TableViewProperties:
|
|
|
|
setCommon(chartKindTable, p.ViewColors, p.DecimalPlaces, p.Queries)
|
|
|
|
setNoteFixes(p.Note, p.ShowNoteWhenEmpty, "", "")
|
|
|
|
ch.TimeFormat = p.TimeFormat
|
|
|
|
ch.TableOptions = tableOptions{
|
|
|
|
VerticalTimeAxis: p.TableOptions.VerticalTimeAxis,
|
|
|
|
SortByField: p.TableOptions.SortBy.InternalName,
|
|
|
|
Wrapping: p.TableOptions.Wrapping,
|
|
|
|
FixFirstColumn: p.TableOptions.FixFirstColumn,
|
|
|
|
}
|
|
|
|
for _, fieldOpt := range p.FieldOptions {
|
|
|
|
ch.FieldOptions = append(ch.FieldOptions, fieldOption{
|
|
|
|
FieldName: fieldOpt.InternalName,
|
|
|
|
DisplayName: fieldOpt.DisplayName,
|
|
|
|
Visible: fieldOpt.Visible,
|
|
|
|
})
|
|
|
|
}
|
2020-08-17 07:07:08 +00:00
|
|
|
case influxdb.BandViewProperties:
|
|
|
|
setCommon(chartKindBand, p.ViewColors, influxdb.DecimalPlaces{}, p.Queries)
|
|
|
|
setNoteFixes(p.Note, p.ShowNoteWhenEmpty, "", "")
|
|
|
|
setLegend(p.Legend)
|
|
|
|
ch.Axes = convertAxes(p.Axes)
|
|
|
|
ch.Geom = p.Geom
|
|
|
|
ch.HoverDimension = p.HoverDimension
|
|
|
|
ch.XCol = p.XColumn
|
|
|
|
ch.YCol = p.YColumn
|
|
|
|
ch.UpperColumn = p.UpperColumn
|
|
|
|
ch.LowerColumn = p.LowerColumn
|
2019-11-08 19:33:41 +00:00
|
|
|
case influxdb.XYViewProperties:
|
|
|
|
setCommon(chartKindXY, p.ViewColors, influxdb.DecimalPlaces{}, p.Queries)
|
|
|
|
setNoteFixes(p.Note, p.ShowNoteWhenEmpty, "", "")
|
|
|
|
setLegend(p.Legend)
|
|
|
|
ch.Axes = convertAxes(p.Axes)
|
|
|
|
ch.Geom = p.Geom
|
|
|
|
ch.Shade = p.ShadeBelow
|
2020-06-17 14:16:46 +00:00
|
|
|
ch.HoverDimension = p.HoverDimension
|
2019-11-08 19:33:41 +00:00
|
|
|
ch.XCol = p.XColumn
|
|
|
|
ch.YCol = p.YColumn
|
2019-12-03 22:59:07 +00:00
|
|
|
ch.Position = p.Position
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
|
|
|
|
2020-06-24 04:30:26 +00:00
|
|
|
sort.Slice(ch.Axes, func(i, j int) bool {
|
|
|
|
return ch.Axes[i].Name < ch.Axes[j].Name
|
|
|
|
})
|
2019-11-08 19:33:41 +00:00
|
|
|
return ch
|
|
|
|
}
|
|
|
|
|
|
|
|
func convertChartToResource(ch chart) Resource {
|
|
|
|
r := Resource{
|
2020-04-13 18:45:20 +00:00
|
|
|
fieldKind: ch.Kind.title(),
|
|
|
|
fieldName: ch.Name,
|
|
|
|
fieldChartHeight: ch.Height,
|
|
|
|
fieldChartWidth: ch.Width,
|
|
|
|
}
|
2020-07-30 18:26:17 +00:00
|
|
|
var qq []Resource
|
|
|
|
for _, q := range ch.Queries {
|
|
|
|
qq = append(qq, Resource{
|
|
|
|
fieldQuery: q.DashboardQuery(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if len(qq) > 0 {
|
|
|
|
r[fieldChartQueries] = qq
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
|
|
|
if len(ch.Colors) > 0 {
|
|
|
|
r[fieldChartColors] = ch.Colors
|
|
|
|
}
|
|
|
|
if len(ch.Axes) > 0 {
|
|
|
|
r[fieldChartAxes] = ch.Axes
|
|
|
|
}
|
2020-08-04 23:24:58 +00:00
|
|
|
if len(ch.YSeriesColumns) > 0 {
|
|
|
|
r[fieldChartYSeriesColumns] = ch.YSeriesColumns
|
|
|
|
}
|
2020-08-17 07:07:08 +00:00
|
|
|
if len(ch.UpperColumn) > 0 {
|
|
|
|
r[fieldChartUpperColumn] = ch.UpperColumn
|
|
|
|
}
|
|
|
|
if len(ch.LowerColumn) > 0 {
|
|
|
|
r[fieldChartLowerColumn] = ch.LowerColumn
|
|
|
|
}
|
2019-11-08 19:33:41 +00:00
|
|
|
if ch.EnforceDecimals {
|
|
|
|
r[fieldChartDecimalPlaces] = ch.DecimalPlaces
|
|
|
|
}
|
|
|
|
|
|
|
|
if ch.Legend.Type != "" {
|
|
|
|
r[fieldChartLegend] = ch.Legend
|
|
|
|
}
|
|
|
|
|
2020-06-10 12:57:09 +00:00
|
|
|
if len(ch.FillColumns) > 0 {
|
|
|
|
r[fieldChartFillColumns] = ch.FillColumns
|
|
|
|
}
|
|
|
|
|
2020-03-04 21:55:15 +00:00
|
|
|
if zero := new(tableOptions); ch.TableOptions != *zero {
|
|
|
|
tRes := make(Resource)
|
|
|
|
assignNonZeroBools(tRes, map[string]bool{
|
|
|
|
fieldChartTableOptionVerticalTimeAxis: ch.TableOptions.VerticalTimeAxis,
|
2020-04-13 18:45:20 +00:00
|
|
|
fieldChartTableOptionFixFirstColumn: ch.TableOptions.FixFirstColumn,
|
2020-03-04 21:55:15 +00:00
|
|
|
})
|
|
|
|
assignNonZeroStrings(tRes, map[string]string{
|
|
|
|
fieldChartTableOptionSortBy: ch.TableOptions.SortByField,
|
|
|
|
fieldChartTableOptionWrapping: ch.TableOptions.Wrapping,
|
|
|
|
})
|
|
|
|
r[fieldChartTableOptions] = tRes
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(ch.FieldOptions) > 0 {
|
|
|
|
fieldOpts := make([]Resource, 0, len(ch.FieldOptions))
|
|
|
|
for _, fo := range ch.FieldOptions {
|
|
|
|
fRes := make(Resource)
|
|
|
|
assignNonZeroBools(fRes, map[string]bool{
|
|
|
|
fieldChartFieldOptionVisible: fo.Visible,
|
|
|
|
})
|
|
|
|
assignNonZeroStrings(fRes, map[string]string{
|
|
|
|
fieldChartFieldOptionDisplayName: fo.DisplayName,
|
|
|
|
fieldChartFieldOptionFieldName: fo.FieldName,
|
|
|
|
})
|
|
|
|
fieldOpts = append(fieldOpts, fRes)
|
|
|
|
}
|
|
|
|
r[fieldChartFieldOptions] = fieldOpts
|
|
|
|
}
|
|
|
|
|
2019-12-05 00:17:35 +00:00
|
|
|
assignNonZeroBools(r, map[string]bool{
|
2019-11-08 19:33:41 +00:00
|
|
|
fieldChartNoteOnEmpty: ch.NoteOnEmpty,
|
|
|
|
fieldChartShade: ch.Shade,
|
2019-12-05 00:17:35 +00:00
|
|
|
})
|
2019-11-08 19:33:41 +00:00
|
|
|
|
2019-12-05 00:17:35 +00:00
|
|
|
assignNonZeroStrings(r, map[string]string{
|
2020-06-17 14:16:46 +00:00
|
|
|
fieldChartNote: ch.Note,
|
|
|
|
fieldPrefix: ch.Prefix,
|
|
|
|
fieldSuffix: ch.Suffix,
|
|
|
|
fieldChartGeom: ch.Geom,
|
|
|
|
fieldChartXCol: ch.XCol,
|
|
|
|
fieldChartYCol: ch.YCol,
|
|
|
|
fieldChartPosition: ch.Position,
|
|
|
|
fieldChartTickPrefix: ch.TickPrefix,
|
|
|
|
fieldChartTickSuffix: ch.TickSuffix,
|
|
|
|
fieldChartTimeFormat: ch.TimeFormat,
|
|
|
|
fieldChartHoverDimension: ch.HoverDimension,
|
2019-12-05 00:17:35 +00:00
|
|
|
})
|
2019-11-08 19:33:41 +00:00
|
|
|
|
2019-12-05 00:17:35 +00:00
|
|
|
assignNonZeroInts(r, map[string]int{
|
2019-11-16 20:14:46 +00:00
|
|
|
fieldChartXPos: ch.XPos,
|
|
|
|
fieldChartYPos: ch.YPos,
|
|
|
|
fieldChartBinCount: ch.BinCount,
|
|
|
|
fieldChartBinSize: ch.BinSize,
|
2019-12-05 00:17:35 +00:00
|
|
|
})
|
2019-11-08 19:33:41 +00:00
|
|
|
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func convertAxes(iAxes map[string]influxdb.Axis) axes {
|
|
|
|
out := make(axes, 0, len(iAxes))
|
|
|
|
for name, a := range iAxes {
|
|
|
|
out = append(out, axis{
|
|
|
|
Base: a.Base,
|
|
|
|
Label: a.Label,
|
|
|
|
Name: name,
|
|
|
|
Prefix: a.Prefix,
|
|
|
|
Scale: a.Scale,
|
|
|
|
Suffix: a.Suffix,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
func convertColors(iColors []influxdb.ViewColor) colors {
|
|
|
|
out := make(colors, 0, len(iColors))
|
|
|
|
for _, ic := range iColors {
|
|
|
|
out = append(out, &color{
|
|
|
|
Name: ic.Name,
|
|
|
|
Type: ic.Type,
|
|
|
|
Hex: ic.Hex,
|
|
|
|
Value: flt64Ptr(ic.Value),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
func convertQueries(iQueries []influxdb.DashboardQuery) queries {
|
|
|
|
out := make(queries, 0, len(iQueries))
|
|
|
|
for _, iq := range iQueries {
|
2020-01-23 01:02:08 +00:00
|
|
|
out = append(out, query{Query: strings.TrimSpace(iq.Text)})
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2020-04-01 23:44:17 +00:00
|
|
|
// DashboardToObject converts an influxdb.Dashboard to an Object.
|
2020-04-08 17:55:05 +00:00
|
|
|
func DashboardToObject(name string, dash influxdb.Dashboard) Object {
|
2019-11-08 19:33:41 +00:00
|
|
|
if name == "" {
|
|
|
|
name = dash.Name
|
|
|
|
}
|
|
|
|
|
2019-12-06 17:13:06 +00:00
|
|
|
sort.Slice(dash.Cells, func(i, j int) bool {
|
|
|
|
ic, jc := dash.Cells[i], dash.Cells[j]
|
2019-11-08 19:33:41 +00:00
|
|
|
if ic.X == jc.X {
|
|
|
|
return ic.Y < jc.Y
|
|
|
|
}
|
|
|
|
return ic.X < jc.X
|
|
|
|
})
|
|
|
|
|
2019-12-06 17:13:06 +00:00
|
|
|
charts := make([]Resource, 0, len(dash.Cells))
|
|
|
|
for _, cell := range dash.Cells {
|
2020-06-26 18:23:32 +00:00
|
|
|
if cell.View == nil {
|
2019-11-08 19:33:41 +00:00
|
|
|
continue
|
|
|
|
}
|
2019-12-06 17:13:06 +00:00
|
|
|
ch := convertCellView(*cell)
|
2019-11-08 19:33:41 +00:00
|
|
|
if !ch.Kind.ok() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
charts = append(charts, convertChartToResource(ch))
|
|
|
|
}
|
|
|
|
|
2020-03-18 22:54:02 +00:00
|
|
|
o := newObject(KindDashboard, name)
|
2020-04-13 18:45:20 +00:00
|
|
|
assignNonZeroStrings(o.Spec, map[string]string{
|
|
|
|
fieldDescription: dash.Description,
|
|
|
|
})
|
2020-03-18 22:54:02 +00:00
|
|
|
o.Spec[fieldDashCharts] = charts
|
|
|
|
return o
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
|
|
|
|
2020-04-01 23:44:17 +00:00
|
|
|
// LabelToObject converts an influxdb.Label to an Object.
|
|
|
|
func LabelToObject(name string, l influxdb.Label) Object {
|
2019-11-08 19:33:41 +00:00
|
|
|
if name == "" {
|
|
|
|
name = l.Name
|
|
|
|
}
|
2019-12-05 00:17:35 +00:00
|
|
|
|
2020-03-18 18:47:13 +00:00
|
|
|
o := newObject(KindLabel, name)
|
|
|
|
assignNonZeroStrings(o.Spec, map[string]string{
|
2019-12-05 00:17:35 +00:00
|
|
|
fieldDescription: l.Properties["description"],
|
|
|
|
fieldLabelColor: l.Properties["color"],
|
|
|
|
})
|
2020-03-18 18:47:13 +00:00
|
|
|
return o
|
2019-12-05 00:17:35 +00:00
|
|
|
}
|
|
|
|
|
2020-04-08 17:08:28 +00:00
|
|
|
// NotificationEndpointToObject converts an notification endpoint into a pkger Object.
|
2020-04-06 17:25:20 +00:00
|
|
|
func NotificationEndpointToObject(name string, e influxdb.NotificationEndpoint) Object {
|
2019-12-16 20:10:45 +00:00
|
|
|
if name == "" {
|
|
|
|
name = e.GetName()
|
|
|
|
}
|
2020-03-19 00:05:29 +00:00
|
|
|
|
|
|
|
o := newObject(KindNotificationEndpoint, name)
|
|
|
|
assignNonZeroStrings(o.Spec, map[string]string{
|
2019-12-16 20:10:45 +00:00
|
|
|
fieldDescription: e.GetDescription(),
|
|
|
|
fieldStatus: string(e.GetStatus()),
|
|
|
|
})
|
|
|
|
|
|
|
|
switch actual := e.(type) {
|
|
|
|
case *endpoint.HTTP:
|
2020-03-19 00:05:29 +00:00
|
|
|
o.Kind = KindNotificationEndpointHTTP
|
|
|
|
o.Spec[fieldNotificationEndpointHTTPMethod] = actual.Method
|
|
|
|
o.Spec[fieldNotificationEndpointURL] = actual.URL
|
|
|
|
o.Spec[fieldType] = actual.AuthMethod
|
|
|
|
assignNonZeroSecrets(o.Spec, map[string]influxdb.SecretField{
|
2019-12-16 20:10:45 +00:00
|
|
|
fieldNotificationEndpointPassword: actual.Password,
|
|
|
|
fieldNotificationEndpointToken: actual.Token,
|
|
|
|
fieldNotificationEndpointUsername: actual.Username,
|
|
|
|
})
|
|
|
|
case *endpoint.PagerDuty:
|
2020-03-19 00:05:29 +00:00
|
|
|
o.Kind = KindNotificationEndpointPagerDuty
|
|
|
|
o.Spec[fieldNotificationEndpointURL] = actual.ClientURL
|
|
|
|
assignNonZeroSecrets(o.Spec, map[string]influxdb.SecretField{
|
2019-12-16 20:10:45 +00:00
|
|
|
fieldNotificationEndpointRoutingKey: actual.RoutingKey,
|
|
|
|
})
|
|
|
|
case *endpoint.Slack:
|
2020-03-19 00:05:29 +00:00
|
|
|
o.Kind = KindNotificationEndpointSlack
|
|
|
|
o.Spec[fieldNotificationEndpointURL] = actual.URL
|
|
|
|
assignNonZeroSecrets(o.Spec, map[string]influxdb.SecretField{
|
2019-12-16 20:10:45 +00:00
|
|
|
fieldNotificationEndpointToken: actual.Token,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-19 00:05:29 +00:00
|
|
|
return o
|
2019-12-16 20:10:45 +00:00
|
|
|
}
|
|
|
|
|
2020-04-08 17:08:28 +00:00
|
|
|
// NotificationRuleToObject converts an notification rule into a pkger Object.
|
|
|
|
func NotificationRuleToObject(name, endpointPkgName string, iRule influxdb.NotificationRule) Object {
|
2019-12-20 20:51:27 +00:00
|
|
|
if name == "" {
|
|
|
|
name = iRule.GetName()
|
|
|
|
}
|
2020-03-19 00:05:29 +00:00
|
|
|
|
|
|
|
o := newObject(KindNotificationRule, name)
|
2020-04-08 17:08:28 +00:00
|
|
|
o.Spec[fieldNotificationRuleEndpointName] = endpointPkgName
|
2020-03-19 00:05:29 +00:00
|
|
|
assignNonZeroStrings(o.Spec, map[string]string{
|
2019-12-20 20:51:27 +00:00
|
|
|
fieldDescription: iRule.GetDescription(),
|
|
|
|
})
|
|
|
|
|
|
|
|
assignBase := func(base rule.Base) {
|
2020-03-19 00:05:29 +00:00
|
|
|
assignNonZeroFluxDurs(o.Spec, map[string]*notification.Duration{
|
2019-12-20 20:51:27 +00:00
|
|
|
fieldEvery: base.Every,
|
|
|
|
fieldOffset: base.Offset,
|
|
|
|
})
|
|
|
|
|
|
|
|
var tagRes []Resource
|
|
|
|
for _, tRule := range base.TagRules {
|
|
|
|
tagRes = append(tagRes, Resource{
|
|
|
|
fieldKey: tRule.Key,
|
|
|
|
fieldValue: tRule.Value,
|
|
|
|
fieldOperator: tRule.Operator.String(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if len(tagRes) > 0 {
|
2020-03-19 00:05:29 +00:00
|
|
|
o.Spec[fieldNotificationRuleTagRules] = tagRes
|
2019-12-20 20:51:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var statusRuleRes []Resource
|
|
|
|
for _, sRule := range base.StatusRules {
|
|
|
|
sRes := Resource{
|
|
|
|
fieldNotificationRuleCurrentLevel: sRule.CurrentLevel.String(),
|
|
|
|
}
|
|
|
|
if sRule.PreviousLevel != nil {
|
|
|
|
sRes[fieldNotificationRulePreviousLevel] = sRule.PreviousLevel.String()
|
|
|
|
}
|
|
|
|
statusRuleRes = append(statusRuleRes, sRes)
|
|
|
|
}
|
|
|
|
if len(statusRuleRes) > 0 {
|
2020-03-19 00:05:29 +00:00
|
|
|
o.Spec[fieldNotificationRuleStatusRules] = statusRuleRes
|
2019-12-20 20:51:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch t := iRule.(type) {
|
|
|
|
case *rule.HTTP:
|
|
|
|
assignBase(t.Base)
|
|
|
|
case *rule.PagerDuty:
|
|
|
|
assignBase(t.Base)
|
2020-03-19 00:05:29 +00:00
|
|
|
o.Spec[fieldNotificationRuleMessageTemplate] = t.MessageTemplate
|
2019-12-20 20:51:27 +00:00
|
|
|
case *rule.Slack:
|
|
|
|
assignBase(t.Base)
|
2020-03-19 00:05:29 +00:00
|
|
|
o.Spec[fieldNotificationRuleMessageTemplate] = t.MessageTemplate
|
|
|
|
assignNonZeroStrings(o.Spec, map[string]string{fieldNotificationRuleChannel: t.Channel})
|
2019-12-20 20:51:27 +00:00
|
|
|
}
|
|
|
|
|
2020-03-19 00:05:29 +00:00
|
|
|
return o
|
2019-12-20 20:51:27 +00:00
|
|
|
}
|
|
|
|
|
2019-12-23 22:31:56 +00:00
|
|
|
// regex used to rip out the hard coded task option stuffs
|
2019-12-27 22:41:00 +00:00
|
|
|
var taskFluxRegex = regexp.MustCompile(`option task = {(.|\n)*?}`)
|
2019-12-23 22:31:56 +00:00
|
|
|
|
2020-04-21 22:00:29 +00:00
|
|
|
// TaskToObject converts an influxdb.Task into a pkger.Object.
|
feat(pkger): add stateful management for tasks
notes on this commit. This commit was grueling ;-(. The task API is not a friendly
API to consume. There are a lot of non obvious things going on and almost every
one of them tripped me up. Things of note:
* the http.TaskService does not satisfy the influxdb.TaskService,
making it impossible to use as a dependency if tasks service gets
split out
* the APIs for create and update do not share common types. For example:
creating a task takes every field as a string, but in the update it is
taken as a options.Duration type. A step further and you'll notice that
create does not need an option to be provided, but the update does. Its
jarring trying to understand the indirection here. I struggled mightily
trying to make sense of it all with the indirection and differing types.
Made for a very difficult task (no pun intended) when it should have been
trivial. Opportunity here to fix these up and make this API more uniform
and remove unneccesary complexity like the options type.
* Nested IDs that get marshaled, are no bueno when you want to marshal a task
that does not have an ID in it, for either user/org/or self IDs. Its a challenge
just to do that.
* Lots of logs in the kv.Task portion where we hit errors and log and others where
we return. It isn't clear what is happening. The kv implementation is also very
procedural, and I found myself bouncing around like a ping pong ball trying to
make heads or tails of it.
* There is auth buried deep inside the kv.Task implementation that kept throwing me
off b/c it kept throwing errors, instead of warns. I assume, not sure if I'm
correct on this, but that the stuff being logged is determined inconsequential
to the task working. I had lots of errors from the auth buried in there, and hadn't
a clue what to make of it....
leaving these notes here as a look back at why working with tasks is so
difficult. This API can improve dramatically. I spent 5x the time trying
to figure out how to use the task API, in procedural calls, than I did
writing the business logic to consume it.... that's a scary realization ;-(
references: #17434
2020-04-21 02:59:56 +00:00
|
|
|
func TaskToObject(name string, t influxdb.Task) Object {
|
2019-12-23 22:31:56 +00:00
|
|
|
if name == "" {
|
|
|
|
name = t.Name
|
|
|
|
}
|
|
|
|
|
2019-12-27 22:41:00 +00:00
|
|
|
query := strings.TrimSpace(taskFluxRegex.ReplaceAllString(t.Flux, ""))
|
2019-12-23 22:31:56 +00:00
|
|
|
|
2020-03-19 16:36:54 +00:00
|
|
|
o := newObject(KindTask, name)
|
|
|
|
assignNonZeroStrings(o.Spec, map[string]string{
|
2019-12-23 22:31:56 +00:00
|
|
|
fieldTaskCron: t.Cron,
|
|
|
|
fieldDescription: t.Description,
|
|
|
|
fieldEvery: t.Every,
|
|
|
|
fieldOffset: durToStr(t.Offset),
|
2020-03-19 16:36:54 +00:00
|
|
|
fieldQuery: strings.TrimSpace(query),
|
2019-12-23 22:31:56 +00:00
|
|
|
})
|
2020-03-19 16:36:54 +00:00
|
|
|
return o
|
2019-12-23 22:31:56 +00:00
|
|
|
}
|
|
|
|
|
2020-04-21 22:00:29 +00:00
|
|
|
// TelegrafToObject converts an influxdb.TelegrafConfig into a pkger.Object.
|
|
|
|
func TelegrafToObject(name string, t influxdb.TelegrafConfig) Object {
|
2019-12-05 00:17:35 +00:00
|
|
|
if name == "" {
|
|
|
|
name = t.Name
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
2020-03-18 18:47:13 +00:00
|
|
|
|
|
|
|
o := newObject(KindTelegraf, name)
|
|
|
|
assignNonZeroStrings(o.Spec, map[string]string{
|
|
|
|
fieldTelegrafConfig: t.Config,
|
|
|
|
fieldDescription: t.Description,
|
2019-12-05 00:17:35 +00:00
|
|
|
})
|
2020-03-18 18:47:13 +00:00
|
|
|
return o
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
|
|
|
|
2020-01-13 19:13:37 +00:00
|
|
|
// VariableToObject converts an influxdb.Variable to a pkger.Object.
|
2020-04-03 00:44:27 +00:00
|
|
|
func VariableToObject(name string, v influxdb.Variable) Object {
|
2019-11-08 19:33:41 +00:00
|
|
|
if name == "" {
|
|
|
|
name = v.Name
|
|
|
|
}
|
|
|
|
|
2020-03-18 18:47:13 +00:00
|
|
|
o := newObject(KindVariable, name)
|
|
|
|
|
|
|
|
assignNonZeroStrings(o.Spec, map[string]string{fieldDescription: v.Description})
|
2019-11-21 00:38:12 +00:00
|
|
|
|
2020-06-22 22:44:53 +00:00
|
|
|
if len(v.Selected) > 0 {
|
|
|
|
o.Spec[fieldVariableSelected] = v.Selected
|
|
|
|
}
|
|
|
|
|
2019-11-08 19:33:41 +00:00
|
|
|
args := v.Arguments
|
|
|
|
if args == nil {
|
2020-03-18 18:47:13 +00:00
|
|
|
return o
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
2020-03-18 18:47:13 +00:00
|
|
|
o.Spec[fieldType] = args.Type
|
2019-11-08 19:33:41 +00:00
|
|
|
|
|
|
|
switch args.Type {
|
|
|
|
case fieldArgTypeConstant:
|
|
|
|
vals, ok := args.Values.(influxdb.VariableConstantValues)
|
|
|
|
if ok {
|
2020-03-18 18:47:13 +00:00
|
|
|
o.Spec[fieldValues] = []string(vals)
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
|
|
|
case fieldArgTypeMap:
|
|
|
|
vals, ok := args.Values.(influxdb.VariableMapValues)
|
|
|
|
if ok {
|
2020-03-18 18:47:13 +00:00
|
|
|
o.Spec[fieldValues] = map[string]string(vals)
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
|
|
|
case fieldArgTypeQuery:
|
|
|
|
vals, ok := args.Values.(influxdb.VariableQueryValues)
|
|
|
|
if ok {
|
2020-03-18 18:47:13 +00:00
|
|
|
o.Spec[fieldLanguage] = vals.Language
|
|
|
|
o.Spec[fieldQuery] = strings.TrimSpace(vals.Query)
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 18:47:13 +00:00
|
|
|
return o
|
2019-11-08 19:33:41 +00:00
|
|
|
}
|
2019-11-15 01:05:21 +00:00
|
|
|
|
2020-03-18 18:47:13 +00:00
|
|
|
func newObject(kind Kind, name string) Object {
|
|
|
|
return Object{
|
|
|
|
APIVersion: APIVersion,
|
|
|
|
Kind: kind,
|
|
|
|
Metadata: Resource{
|
|
|
|
// this timestamp is added to make the resource unique. Should also indicate
|
|
|
|
// to the end user that this is machine readable and the spec.name field is
|
|
|
|
// the one they want to edit when a name change is desired.
|
2020-05-03 17:34:24 +00:00
|
|
|
fieldName: strings.ToLower(idGenerator.ID().String()),
|
2020-03-18 18:47:13 +00:00
|
|
|
},
|
|
|
|
Spec: Resource{
|
|
|
|
fieldName: name,
|
|
|
|
},
|
2020-02-05 00:15:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-20 20:51:27 +00:00
|
|
|
func assignNonZeroFluxDurs(r Resource, m map[string]*notification.Duration) {
|
|
|
|
for field, dur := range m {
|
|
|
|
if dur == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if dur.TimeDuration() == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
r[field] = dur.TimeDuration().String()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-05 00:17:35 +00:00
|
|
|
func assignNonZeroBools(r Resource, m map[string]bool) {
|
|
|
|
for k, v := range m {
|
|
|
|
if v {
|
|
|
|
r[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func assignNonZeroInts(r Resource, m map[string]int) {
|
|
|
|
for k, v := range m {
|
|
|
|
if v != 0 {
|
|
|
|
r[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func assignNonZeroStrings(r Resource, m map[string]string) {
|
|
|
|
for k, v := range m {
|
|
|
|
if v != "" {
|
|
|
|
r[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-16 20:10:45 +00:00
|
|
|
func assignNonZeroSecrets(r Resource, m map[string]influxdb.SecretField) {
|
|
|
|
for field, secret := range m {
|
|
|
|
if secret.Key == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
r[field] = Resource{
|
|
|
|
fieldReferencesSecret: Resource{
|
|
|
|
fieldKey: secret.Key,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-15 01:05:21 +00:00
|
|
|
func stringsToColors(clrs []string) colors {
|
|
|
|
newColors := make(colors, 0)
|
|
|
|
for _, x := range clrs {
|
|
|
|
newColors = append(newColors, &color{Hex: x})
|
|
|
|
}
|
|
|
|
return newColors
|
|
|
|
}
|