feat(pkger): add ability to export resources by name from cli (#19457)
* feat(pkger): add ability to export dashboards by name * chore: cleanup types * chore: actually restrict exported dashboards to specified name * feat: export buckets by name * feat: export checks by name * feat: export labels by name * feat: export notification endpoints by name * feat: export notification rules by name * feat: export tasks by name * feat: export telegraf configs by name * feat: export variables by name * chore: remove name from service clone org resources these functions are not hit by the cli * chore: update old tests and add new tests * chore: revert notificationEndpoints to be name unique * chore: address feedback * chore: define template export by name in swagger * chore: proper swagger syntax * chore: remove crufty comments * chore: fix typo in cli flag * chore: update changelogpull/19625/head
commit
85d75e3d4e
|
@ -34,6 +34,7 @@ need to update any InfluxDB CLI config profiles with the new port number.
|
|||
1. [19433](https://github.com/influxdata/influxdb/pull/19433): Add option to dump raw query results in CLI
|
||||
1. [19506](https://github.com/influxdata/influxdb/pull/19506): Add TSM 1.x storage options as flags
|
||||
1. [19508](https://github.com/influxdata/influxdb/pull/19508): Add subset of InfluxQL coordinator options as flags
|
||||
1. [19457](https://github.com/influxdata/influxdb/pull/19457): Add ability to export resources by name via the CLI
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
|
|
|
@ -77,16 +77,25 @@ type cmdTemplateBuilder struct {
|
|||
}
|
||||
|
||||
exportOpts struct {
|
||||
resourceType string
|
||||
buckets string
|
||||
checks string
|
||||
dashboards string
|
||||
endpoints string
|
||||
labels string
|
||||
rules string
|
||||
tasks string
|
||||
telegrafs string
|
||||
variables string
|
||||
resourceType string
|
||||
buckets string
|
||||
checks string
|
||||
dashboards string
|
||||
endpoints string
|
||||
labels string
|
||||
rules string
|
||||
tasks string
|
||||
telegrafs string
|
||||
variables string
|
||||
bucketNames string
|
||||
checkNames string
|
||||
dashboardNames string
|
||||
endpointNames string
|
||||
labelNames string
|
||||
ruleNames string
|
||||
taskNames string
|
||||
telegrafNames string
|
||||
variableNames string
|
||||
}
|
||||
|
||||
updateStackOpts struct {
|
||||
|
@ -351,6 +360,15 @@ func (b *cmdTemplateBuilder) cmdExport() *cobra.Command {
|
|||
cmd.Flags().StringVar(&b.exportOpts.tasks, "tasks", "", "List of task ids comma separated")
|
||||
cmd.Flags().StringVar(&b.exportOpts.telegrafs, "telegraf-configs", "", "List of telegraf config ids comma separated")
|
||||
cmd.Flags().StringVar(&b.exportOpts.variables, "variables", "", "List of variable ids comma separated")
|
||||
cmd.Flags().StringVar(&b.exportOpts.bucketNames, "bucket-names", "", "List of bucket names comma separated")
|
||||
cmd.Flags().StringVar(&b.exportOpts.checkNames, "check-names", "", "List of check names comma separated")
|
||||
cmd.Flags().StringVar(&b.exportOpts.dashboardNames, "dashboard-names", "", "List of dashboard names comma separated")
|
||||
cmd.Flags().StringVar(&b.exportOpts.endpointNames, "endpoint-names", "", "List of notification endpoint names comma separated")
|
||||
cmd.Flags().StringVar(&b.exportOpts.labelNames, "label-names", "", "List of label names comma separated")
|
||||
cmd.Flags().StringVar(&b.exportOpts.ruleNames, "rule-names", "", "List of notification rule names comma separated")
|
||||
cmd.Flags().StringVar(&b.exportOpts.taskNames, "task-names", "", "List of task names comma separated")
|
||||
cmd.Flags().StringVar(&b.exportOpts.telegrafNames, "telegraf-config-names", "", "List of telegraf config names comma separated")
|
||||
cmd.Flags().StringVar(&b.exportOpts.variableNames, "variable-names", "", "List of variable names comma separated")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -364,21 +382,22 @@ func (b *cmdTemplateBuilder) exportRunEFn(cmd *cobra.Command, args []string) err
|
|||
resTypes := []struct {
|
||||
kind pkger.Kind
|
||||
idStrs []string
|
||||
names []string
|
||||
}{
|
||||
{kind: pkger.KindBucket, idStrs: strings.Split(b.exportOpts.buckets, ",")},
|
||||
{kind: pkger.KindCheck, idStrs: strings.Split(b.exportOpts.checks, ",")},
|
||||
{kind: pkger.KindDashboard, idStrs: strings.Split(b.exportOpts.dashboards, ",")},
|
||||
{kind: pkger.KindLabel, idStrs: strings.Split(b.exportOpts.labels, ",")},
|
||||
{kind: pkger.KindNotificationEndpoint, idStrs: strings.Split(b.exportOpts.endpoints, ",")},
|
||||
{kind: pkger.KindNotificationRule, idStrs: strings.Split(b.exportOpts.rules, ",")},
|
||||
{kind: pkger.KindTask, idStrs: strings.Split(b.exportOpts.tasks, ",")},
|
||||
{kind: pkger.KindTelegraf, idStrs: strings.Split(b.exportOpts.telegrafs, ",")},
|
||||
{kind: pkger.KindVariable, idStrs: strings.Split(b.exportOpts.variables, ",")},
|
||||
{kind: pkger.KindBucket, idStrs: strings.Split(b.exportOpts.buckets, ","), names: strings.Split(b.exportOpts.bucketNames, ",")},
|
||||
{kind: pkger.KindCheck, idStrs: strings.Split(b.exportOpts.checks, ","), names: strings.Split(b.exportOpts.checkNames, ",")},
|
||||
{kind: pkger.KindDashboard, idStrs: strings.Split(b.exportOpts.dashboards, ","), names: strings.Split(b.exportOpts.dashboardNames, ",")},
|
||||
{kind: pkger.KindLabel, idStrs: strings.Split(b.exportOpts.labels, ","), names: strings.Split(b.exportOpts.labelNames, ",")},
|
||||
{kind: pkger.KindNotificationEndpoint, idStrs: strings.Split(b.exportOpts.endpoints, ","), names: strings.Split(b.exportOpts.endpointNames, ",")},
|
||||
{kind: pkger.KindNotificationRule, idStrs: strings.Split(b.exportOpts.rules, ","), names: strings.Split(b.exportOpts.ruleNames, ",")},
|
||||
{kind: pkger.KindTask, idStrs: strings.Split(b.exportOpts.tasks, ","), names: strings.Split(b.exportOpts.taskNames, ",")},
|
||||
{kind: pkger.KindTelegraf, idStrs: strings.Split(b.exportOpts.telegrafs, ","), names: strings.Split(b.exportOpts.telegrafNames, ",")},
|
||||
{kind: pkger.KindVariable, idStrs: strings.Split(b.exportOpts.variables, ","), names: strings.Split(b.exportOpts.variableNames, ",")},
|
||||
}
|
||||
|
||||
var opts []pkger.ExportOptFn
|
||||
for _, rt := range resTypes {
|
||||
newOpt, err := newResourcesToClone(rt.kind, rt.idStrs)
|
||||
newOpt, err := newResourcesToClone(rt.kind, rt.idStrs, rt.names)
|
||||
if err != nil {
|
||||
return ierror.Wrap(err, rt.kind.String())
|
||||
}
|
||||
|
@ -410,7 +429,7 @@ func (b *cmdTemplateBuilder) exportRunEFn(cmd *cobra.Command, args []string) err
|
|||
}
|
||||
}
|
||||
|
||||
resTypeOpt, err := newResourcesToClone(resKind, args)
|
||||
resTypeOpt, err := newResourcesToClone(resKind, args, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1133,7 +1152,7 @@ func (b *cmdTemplateBuilder) convertEncoding() pkger.Encoding {
|
|||
}
|
||||
}
|
||||
|
||||
func newResourcesToClone(kind pkger.Kind, idStrs []string) (pkger.ExportOptFn, error) {
|
||||
func newResourcesToClone(kind pkger.Kind, idStrs, names []string) (pkger.ExportOptFn, error) {
|
||||
ids, err := toInfluxIDs(idStrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1146,6 +1165,15 @@ func newResourcesToClone(kind pkger.Kind, idStrs []string) (pkger.ExportOptFn, e
|
|||
ID: id,
|
||||
})
|
||||
}
|
||||
for _, name := range names {
|
||||
if len(name) == 0 {
|
||||
continue
|
||||
}
|
||||
resources = append(resources, pkger.ResourceToClone{
|
||||
Kind: kind,
|
||||
Name: name,
|
||||
})
|
||||
}
|
||||
return pkger.ExportWithExistingResources(resources...), nil
|
||||
}
|
||||
|
||||
|
@ -1156,7 +1184,7 @@ func toInfluxIDs(args []string) ([]influxdb.ID, error) {
|
|||
)
|
||||
for _, arg := range args {
|
||||
normedArg := strings.TrimSpace(strings.ToLower(arg))
|
||||
if normedArg == "" {
|
||||
if len(normedArg) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -4602,7 +4602,9 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/TemplateExport"
|
||||
oneOf:
|
||||
- $ref: "#/components/schemas/TemplateExportByID"
|
||||
- $ref: "#/components/schemas/TemplateExportByName"
|
||||
responses:
|
||||
"200":
|
||||
description: InfluxDB template created
|
||||
|
@ -7475,7 +7477,7 @@ components:
|
|||
- Task
|
||||
- Telegraf
|
||||
- Variable
|
||||
TemplateExport:
|
||||
TemplateExportByID:
|
||||
type: object
|
||||
properties:
|
||||
stackID:
|
||||
|
@ -7507,7 +7509,39 @@ components:
|
|||
$ref: "#/components/schemas/TemplateKind"
|
||||
name:
|
||||
type: string
|
||||
description: "if defined with id, name is used for resource exported by id. if defined independently, resources strictly matching name are exported"
|
||||
required: [id, kind]
|
||||
TemplateExportByName:
|
||||
type: object
|
||||
properties:
|
||||
stackID:
|
||||
type: string
|
||||
orgIDs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
orgID:
|
||||
type: string
|
||||
resourceFilters:
|
||||
type: object
|
||||
properties:
|
||||
byLabel:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
byResourceKind:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/TemplateKind"
|
||||
resources:
|
||||
type: object
|
||||
properties:
|
||||
kind:
|
||||
$ref: "#/components/schemas/TemplateKind"
|
||||
name:
|
||||
type: string
|
||||
required: [name, kind]
|
||||
Template:
|
||||
type: array
|
||||
items:
|
||||
|
|
|
@ -135,7 +135,9 @@ func filterDashboardsFn(filter influxdb.DashboardFilter) func(d *influxdb.Dashbo
|
|||
}
|
||||
}
|
||||
|
||||
return func(d *influxdb.Dashboard) bool { return true }
|
||||
return func(d *influxdb.Dashboard) bool {
|
||||
return ((filter.OrganizationID == nil) || (*filter.OrganizationID == d.OrganizationID))
|
||||
}
|
||||
}
|
||||
|
||||
// FindDashboards retrives all dashboards that match an arbitrary dashboard filter.
|
||||
|
|
|
@ -27,7 +27,7 @@ type NameGenerator func() string
|
|||
// ResourceToClone is a resource that will be cloned.
|
||||
type ResourceToClone struct {
|
||||
Kind Kind `json:"kind"`
|
||||
ID influxdb.ID `json:"id"`
|
||||
ID influxdb.ID `json:"id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
// 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
|
||||
|
@ -40,8 +40,8 @@ func (r ResourceToClone) OK() error {
|
|||
if err := r.Kind.OK(); err != nil {
|
||||
return err
|
||||
}
|
||||
if r.ID == influxdb.ID(0) {
|
||||
return errors.New("must provide an ID")
|
||||
if r.ID == influxdb.ID(0) && len(r.Name) == 0 {
|
||||
return errors.New("must provide an ID or name")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -171,13 +171,10 @@ func (ex *resourceExporter) StackResources() []StackResource {
|
|||
return resources
|
||||
}
|
||||
|
||||
func (ex *resourceExporter) uniqByNameResID() influxdb.ID {
|
||||
// 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 = 0
|
||||
return uniqByNameResID
|
||||
}
|
||||
// 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)
|
||||
|
||||
type cloneAssociationsFn func(context.Context, ResourceToClone) (associations []ObjectAssociation, skipResource bool, err error)
|
||||
|
||||
|
@ -220,77 +217,255 @@ func (ex *resourceExporter) resourceCloneToKind(ctx context.Context, r ResourceT
|
|||
ex.mStackResources[key] = stackResource
|
||||
}
|
||||
|
||||
uniqByNameResID := ex.uniqByNameResID()
|
||||
|
||||
switch {
|
||||
case r.Kind.is(KindBucket):
|
||||
bkt, err := ex.bucketSVC.FindBucketByID(ctx, r.ID)
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mapResource(bkt.OrgID, uniqByNameResID, KindBucket, BucketToObject(r.Name, *bkt))
|
||||
case r.Kind.is(KindCheck),
|
||||
r.Kind.is(KindCheckDeadman),
|
||||
r.Kind.is(KindCheckThreshold):
|
||||
ch, err := ex.checkSVC.FindCheckByID(ctx, r.ID)
|
||||
if n < 1 {
|
||||
return errors.New("no buckets found")
|
||||
}
|
||||
|
||||
for _, bkt := range bkts {
|
||||
mapResource(bkt.OrgID, bkt.ID, KindBucket, BucketToObject(r.Name, *bkt))
|
||||
}
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mapResource(ch.GetOrgID(), uniqByNameResID, KindCheck, CheckToObject(r.Name, ch))
|
||||
if n < 1 {
|
||||
return errors.New("no checks found")
|
||||
}
|
||||
|
||||
for _, ch := range chs {
|
||||
mapResource(ch.GetOrgID(), ch.GetID(), KindCheck, CheckToObject(r.Name, ch))
|
||||
}
|
||||
case r.Kind.is(KindDashboard):
|
||||
dash, err := findDashboardByIDFull(ctx, ex.dashSVC, r.ID)
|
||||
var (
|
||||
hasID bool
|
||||
filter = influxdb.DashboardFilter{}
|
||||
)
|
||||
if r.ID != influxdb.ID(0) {
|
||||
hasID = true
|
||||
filter.IDs = []*influxdb.ID{&r.ID}
|
||||
}
|
||||
|
||||
dashes, _, err := ex.dashSVC.FindDashboards(ctx, filter, influxdb.DefaultDashboardFindOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mapResource(dash.OrganizationID, dash.ID, KindDashboard, DashboardToObject(r.Name, *dash))
|
||||
|
||||
var mapped bool
|
||||
for _, dash := range dashes {
|
||||
if (!hasID && len(r.Name) > 0 && dash.Name != r.Name) || (hasID && dash.ID != r.ID) {
|
||||
continue
|
||||
}
|
||||
|
||||
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))
|
||||
mapped = true
|
||||
}
|
||||
|
||||
if !mapped {
|
||||
return errors.New("no dashboards found")
|
||||
}
|
||||
case r.Kind.is(KindLabel):
|
||||
l, err := ex.labelSVC.FindLabelByID(ctx, r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
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))
|
||||
}
|
||||
}
|
||||
mapResource(l.OrgID, uniqByNameResID, KindLabel, LabelToObject(r.Name, *l))
|
||||
case r.Kind.is(KindNotificationEndpoint),
|
||||
r.Kind.is(KindNotificationEndpointHTTP),
|
||||
r.Kind.is(KindNotificationEndpointPagerDuty),
|
||||
r.Kind.is(KindNotificationEndpointSlack):
|
||||
e, err := ex.endpointSVC.FindNotificationEndpointByID(ctx, r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
var endpoints []influxdb.NotificationEndpoint
|
||||
|
||||
switch {
|
||||
case r.ID != influxdb.ID(0):
|
||||
notifEndpoint, err := ex.endpointSVC.FindNotificationEndpointByID(ctx, r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoints = append(endpoints, notifEndpoint)
|
||||
case len(r.Name) != 0:
|
||||
allEndpoints, _, err := ex.endpointSVC.FindNotificationEndpoints(ctx, influxdb.NotificationEndpointFilter{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, notifEndpoint := range allEndpoints {
|
||||
if notifEndpoint.GetName() != r.Name || notifEndpoint == nil {
|
||||
continue
|
||||
}
|
||||
endpoints = append(endpoints, notifEndpoint)
|
||||
}
|
||||
}
|
||||
|
||||
if len(endpoints) == 0 {
|
||||
return errors.New("no notification endpoints found")
|
||||
}
|
||||
|
||||
for _, e := range endpoints {
|
||||
mapResource(e.GetOrgID(), uniqByNameResID, KindNotificationEndpoint, NotificationEndpointToObject(r.Name, e))
|
||||
}
|
||||
mapResource(e.GetOrgID(), uniqByNameResID, KindNotificationEndpoint, NotificationEndpointToObject(r.Name, e))
|
||||
case r.Kind.is(KindNotificationRule):
|
||||
rule, ruleEndpoint, err := ex.getEndpointRule(ctx, r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
var rules []influxdb.NotificationRule
|
||||
|
||||
switch {
|
||||
case r.ID != influxdb.ID(0):
|
||||
r, err := ex.ruleSVC.FindNotificationRuleByID(ctx, r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rules = append(rules, r)
|
||||
case len(r.Name) != 0:
|
||||
allRules, _, err := ex.ruleSVC.FindNotificationRules(ctx, influxdb.NotificationRuleFilter{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rule := range allRules {
|
||||
if rule.GetName() != r.Name {
|
||||
continue
|
||||
}
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
if len(rules) == 0 {
|
||||
return errors.New("no notification rules found")
|
||||
}
|
||||
endpointObjectName := object.Name()
|
||||
|
||||
mapResource(rule.GetOrgID(), rule.GetID(), KindNotificationRule, NotificationRuleToObject(r.Name, endpointObjectName, rule))
|
||||
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))
|
||||
}
|
||||
case r.Kind.is(KindTask):
|
||||
t, err := ex.taskSVC.FindTaskByID(ctx, r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
switch {
|
||||
case r.ID != influxdb.ID(0):
|
||||
t, err := ex.taskSVC.FindTaskByID(ctx, r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mapResource(t.OrganizationID, t.ID, KindTask, TaskToObject(r.Name, *t))
|
||||
case len(r.Name) > 0:
|
||||
tasks, n, err := ex.taskSVC.FindTasks(ctx, influxdb.TaskFilter{Name: &r.Name})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n < 1 {
|
||||
return errors.New("no tasks found")
|
||||
}
|
||||
|
||||
for _, t := range tasks {
|
||||
mapResource(t.OrganizationID, t.ID, KindTask, TaskToObject(r.Name, *t))
|
||||
}
|
||||
}
|
||||
mapResource(t.OrganizationID, t.ID, KindTask, TaskToObject(r.Name, *t))
|
||||
case r.Kind.is(KindTelegraf):
|
||||
t, err := ex.teleSVC.FindTelegrafConfigByID(ctx, r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
switch {
|
||||
case r.ID != influxdb.ID(0):
|
||||
t, err := ex.teleSVC.FindTelegrafConfigByID(ctx, r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mapResource(t.OrgID, t.ID, KindTelegraf, TelegrafToObject(r.Name, *t))
|
||||
case len(r.Name) > 0:
|
||||
telegrafs, _, err := ex.teleSVC.FindTelegrafConfigs(ctx, influxdb.TelegrafConfigFilter{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var mapped bool
|
||||
for _, t := range telegrafs {
|
||||
if t.Name != r.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
mapResource(t.OrgID, t.ID, KindTelegraf, TelegrafToObject(r.Name, *t))
|
||||
mapped = true
|
||||
}
|
||||
if !mapped {
|
||||
return errors.New("no telegraf configs found")
|
||||
}
|
||||
|
||||
}
|
||||
mapResource(t.OrgID, t.ID, KindTelegraf, TelegrafToObject(r.Name, *t))
|
||||
case r.Kind.is(KindVariable):
|
||||
v, err := ex.varSVC.FindVariableByID(ctx, r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
switch {
|
||||
case r.ID != influxdb.ID(0):
|
||||
v, err := ex.varSVC.FindVariableByID(ctx, r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mapResource(v.OrganizationID, uniqByNameResID, KindVariable, VariableToObject(r.Name, *v))
|
||||
case len(r.Name) > 0:
|
||||
variables, err := ex.varSVC.FindVariables(ctx, influxdb.VariableFilter{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var mapped bool
|
||||
for _, v := range variables {
|
||||
if v.Name != r.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
mapResource(v.OrganizationID, uniqByNameResID, KindVariable, VariableToObject(r.Name, *v))
|
||||
mapped = true
|
||||
}
|
||||
if !mapped {
|
||||
return errors.New("no variables found")
|
||||
}
|
||||
}
|
||||
mapResource(v.OrganizationID, uniqByNameResID, KindVariable, VariableToObject(r.Name, *v))
|
||||
default:
|
||||
return errors.New("unsupported kind provided: " + string(r.Kind))
|
||||
}
|
||||
|
@ -319,6 +494,10 @@ func (ex *resourceExporter) resourceCloneAssociationsGen(ctx context.Context, la
|
|||
return nil, shouldSkip, nil
|
||||
}
|
||||
|
||||
if len(r.Name) > 0 && r.ID == influxdb.ID(0) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
labels, err := ex.labelSVC.FindResourceLabels(ctx, influxdb.LabelMappingFilter{
|
||||
ResourceID: r.ID,
|
||||
ResourceType: r.Kind.ResourceType(),
|
||||
|
@ -355,7 +534,7 @@ func (ex *resourceExporter) resourceCloneAssociationsGen(ctx context.Context, la
|
|||
}
|
||||
labelObject.Metadata[fieldName] = metaName
|
||||
|
||||
k := newExportKey(l.OrgID, ex.uniqByNameResID(), KindLabel, l.Name)
|
||||
k := newExportKey(l.OrgID, uniqByNameResID, KindLabel, l.Name)
|
||||
existing, ok := ex.mObjects[k]
|
||||
if ok {
|
||||
associations = append(associations, ObjectAssociation{
|
||||
|
@ -379,20 +558,6 @@ func (ex *resourceExporter) resourceCloneAssociationsGen(ctx context.Context, la
|
|||
return cloneFn, nil
|
||||
}
|
||||
|
||||
func (ex *resourceExporter) getEndpointRule(ctx context.Context, id influxdb.ID) (influxdb.NotificationRule, influxdb.NotificationEndpoint, error) {
|
||||
rule, err := ex.ruleSVC.FindNotificationRuleByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ruleEndpoint, err := ex.endpointSVC.FindNotificationEndpointByID(ctx, rule.GetEndpointID())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return rule, ruleEndpoint, nil
|
||||
}
|
||||
|
||||
func (ex *resourceExporter) uniqName() string {
|
||||
return uniqMetaName(ex.nameGen, idGenerator, ex.mPkgNames)
|
||||
}
|
||||
|
@ -409,21 +574,6 @@ func uniqMetaName(nameGen NameGenerator, idGen influxdb.IDGenerator, existingNam
|
|||
return name
|
||||
}
|
||||
|
||||
func findDashboardByIDFull(ctx context.Context, dashSVC influxdb.DashboardService, id influxdb.ID) (*influxdb.Dashboard, error) {
|
||||
dash, err := dashSVC.FindDashboardByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, cell := range dash.Cells {
|
||||
v, err := dashSVC.GetDashboardCellView(ctx, id, cell.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cell.View = v
|
||||
}
|
||||
return dash, nil
|
||||
}
|
||||
|
||||
func uniqResourcesToClone(resources []ResourceToClone) []ResourceToClone {
|
||||
type key struct {
|
||||
kind Kind
|
||||
|
|
|
@ -860,7 +860,8 @@ func (p *Template) graphLabels() *parseErr {
|
|||
|
||||
func (p *Template) graphChecks() *parseErr {
|
||||
p.mChecks = make(map[string]*check)
|
||||
tracker := p.trackNames(true)
|
||||
// todo: what is the business goal wrt having unique names? (currently duplicates are allowed)
|
||||
tracker := p.trackNames(false)
|
||||
|
||||
checkKinds := []struct {
|
||||
kind Kind
|
||||
|
|
|
@ -2123,7 +2123,6 @@ func (v *variable) summarize() SummaryVariable {
|
|||
envRefs = append(envRefs, convertRefToRefSummary(field, sel))
|
||||
}
|
||||
}
|
||||
|
||||
return SummaryVariable{
|
||||
SummaryIdentifier: SummaryIdentifier{
|
||||
Kind: KindVariable,
|
||||
|
|
|
@ -904,40 +904,42 @@ spec:
|
|||
`,
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: KindCheckDeadman,
|
||||
resErr: testTemplateResourceError{
|
||||
name: "duplicate meta name and spec name",
|
||||
validationErrs: 1,
|
||||
valFields: []string{fieldSpec, fieldAssociations},
|
||||
templateStr: `
|
||||
apiVersion: influxdata.com/v2alpha1
|
||||
kind: CheckDeadman
|
||||
metadata:
|
||||
name: check-1
|
||||
spec:
|
||||
every: 5m
|
||||
level: cRiT
|
||||
query: >
|
||||
from(bucket: "rucket_1") |> yield(name: "mean")
|
||||
statusMessageTemplate: "Check: ${ r._check_name } is: ${ r._level }"
|
||||
timeSince: 90s
|
||||
---
|
||||
apiVersion: influxdata.com/v2alpha1
|
||||
kind: CheckDeadman
|
||||
metadata:
|
||||
name: valid-name
|
||||
spec:
|
||||
name: check-1
|
||||
every: 5m
|
||||
level: cRiT
|
||||
query: >
|
||||
from(bucket: "rucket_1") |> yield(name: "mean")
|
||||
statusMessageTemplate: "Check: ${ r._check_name } is: ${ r._level }"
|
||||
timeSince: 90s
|
||||
`,
|
||||
},
|
||||
},
|
||||
/* checks are not name unique
|
||||
{
|
||||
kind: KindCheckDeadman,
|
||||
resErr: testTemplateResourceError{
|
||||
name: "duplicate meta name and spec name",
|
||||
validationErrs: 1,
|
||||
valFields: []string{fieldSpec, fieldAssociations},
|
||||
templateStr: `
|
||||
apiVersion: influxdata.com/v2alpha1
|
||||
kind: CheckDeadman
|
||||
metadata:
|
||||
name: check-1
|
||||
spec:
|
||||
every: 5m
|
||||
level: cRiT
|
||||
query: >
|
||||
from(bucket: "rucket_1") |> yield(name: "mean")
|
||||
statusMessageTemplate: "Check: ${ r._check_name } is: ${ r._level }"
|
||||
timeSince: 90s
|
||||
---
|
||||
apiVersion: influxdata.com/v2alpha1
|
||||
kind: CheckDeadman
|
||||
metadata:
|
||||
name: valid-name
|
||||
spec:
|
||||
name: check-1
|
||||
every: 5m
|
||||
level: cRiT
|
||||
query: >
|
||||
from(bucket: "rucket_1") |> yield(name: "mean")
|
||||
statusMessageTemplate: "Check: ${ r._check_name } is: ${ r._level }"
|
||||
timeSince: 90s
|
||||
`,
|
||||
},
|
||||
},
|
||||
*/
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
|
@ -72,6 +72,7 @@ type (
|
|||
StackResource struct {
|
||||
APIVersion string
|
||||
ID influxdb.ID
|
||||
Name string
|
||||
Kind Kind
|
||||
MetaName string
|
||||
Associations []StackResourceAssociation
|
||||
|
@ -631,6 +632,7 @@ func (s *Service) Export(ctx context.Context, setters ...ExportOptFn) (*Template
|
|||
Kind: r.Kind,
|
||||
ID: r.ID,
|
||||
MetaName: r.MetaName,
|
||||
Name: r.Name,
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -694,6 +696,7 @@ func (s *Service) cloneOrgBuckets(ctx context.Context, orgID influxdb.ID) ([]Res
|
|||
resources = append(resources, ResourceToClone{
|
||||
Kind: KindBucket,
|
||||
ID: b.ID,
|
||||
Name: b.Name,
|
||||
})
|
||||
}
|
||||
return resources, nil
|
||||
|
@ -712,6 +715,7 @@ func (s *Service) cloneOrgChecks(ctx context.Context, orgID influxdb.ID) ([]Reso
|
|||
resources = append(resources, ResourceToClone{
|
||||
Kind: KindCheck,
|
||||
ID: c.GetID(),
|
||||
Name: c.GetName(),
|
||||
})
|
||||
}
|
||||
return resources, nil
|
||||
|
@ -736,9 +740,11 @@ func (s *Service) cloneOrgDashboards(ctx context.Context, orgID influxdb.ID) ([]
|
|||
}
|
||||
|
||||
func (s *Service) cloneOrgLabels(ctx context.Context, orgID influxdb.ID) ([]ResourceToClone, error) {
|
||||
labels, err := s.labelSVC.FindLabels(ctx, influxdb.LabelFilter{
|
||||
filter := influxdb.LabelFilter{
|
||||
OrgID: &orgID,
|
||||
}, influxdb.FindOptions{Limit: 10000})
|
||||
}
|
||||
|
||||
labels, err := s.labelSVC.FindLabels(ctx, filter, influxdb.FindOptions{Limit: 100})
|
||||
if err != nil {
|
||||
return nil, ierrors.Wrap(err, "finding labels")
|
||||
}
|
||||
|
@ -748,6 +754,7 @@ func (s *Service) cloneOrgLabels(ctx context.Context, orgID influxdb.ID) ([]Reso
|
|||
resources = append(resources, ResourceToClone{
|
||||
Kind: KindLabel,
|
||||
ID: l.ID,
|
||||
Name: l.Name,
|
||||
})
|
||||
}
|
||||
return resources, nil
|
||||
|
@ -766,6 +773,7 @@ func (s *Service) cloneOrgNotificationEndpoints(ctx context.Context, orgID influ
|
|||
resources = append(resources, ResourceToClone{
|
||||
Kind: KindNotificationEndpoint,
|
||||
ID: e.GetID(),
|
||||
Name: e.GetName(),
|
||||
})
|
||||
}
|
||||
return resources, nil
|
||||
|
@ -784,6 +792,7 @@ func (s *Service) cloneOrgNotificationRules(ctx context.Context, orgID influxdb.
|
|||
resources = append(resources, ResourceToClone{
|
||||
Kind: KindNotificationRule,
|
||||
ID: r.GetID(),
|
||||
Name: r.GetName(),
|
||||
})
|
||||
}
|
||||
return resources, nil
|
||||
|
@ -869,12 +878,15 @@ func (s *Service) cloneOrgVariables(ctx context.Context, orgID influxdb.ID) ([]R
|
|||
return resources, nil
|
||||
}
|
||||
|
||||
type cloneResFn func(context.Context, influxdb.ID) ([]ResourceToClone, error)
|
||||
type (
|
||||
cloneResFn func(context.Context, influxdb.ID) ([]ResourceToClone, error)
|
||||
resClone struct {
|
||||
resType influxdb.ResourceType
|
||||
cloneFn cloneResFn
|
||||
}
|
||||
)
|
||||
|
||||
func (s *Service) filterOrgResourceKinds(resourceKindFilters []Kind) []struct {
|
||||
resType influxdb.ResourceType
|
||||
cloneFn cloneResFn
|
||||
} {
|
||||
func (s *Service) filterOrgResourceKinds(resourceKindFilters []Kind) []resClone {
|
||||
mKinds := map[Kind]cloneResFn{
|
||||
KindBucket: s.cloneOrgBuckets,
|
||||
KindCheck: s.cloneOrgChecks,
|
||||
|
@ -887,23 +899,14 @@ func (s *Service) filterOrgResourceKinds(resourceKindFilters []Kind) []struct {
|
|||
KindVariable: s.cloneOrgVariables,
|
||||
}
|
||||
|
||||
newResGen := func(resType influxdb.ResourceType, cloneFn cloneResFn) struct {
|
||||
resType influxdb.ResourceType
|
||||
cloneFn cloneResFn
|
||||
} {
|
||||
return struct {
|
||||
resType influxdb.ResourceType
|
||||
cloneFn cloneResFn
|
||||
}{
|
||||
newResGen := func(resType influxdb.ResourceType, cloneFn cloneResFn) resClone {
|
||||
return resClone{
|
||||
resType: resType,
|
||||
cloneFn: cloneFn,
|
||||
}
|
||||
}
|
||||
|
||||
var resourceTypeGens []struct {
|
||||
resType influxdb.ResourceType
|
||||
cloneFn cloneResFn
|
||||
}
|
||||
var resourceTypeGens []resClone
|
||||
if len(resourceKindFilters) == 0 {
|
||||
for k, cloneFn := range mKinds {
|
||||
resourceTypeGens = append(resourceTypeGens, newResGen(k.ResourceType(), cloneFn))
|
||||
|
|
|
@ -148,7 +148,7 @@ func (s *loggingMW) Export(ctx context.Context, opts ...ExportOptFn) (template *
|
|||
s.logger.Error("failed to export template", zap.Error(err), dur)
|
||||
return
|
||||
}
|
||||
s.logger.Info("failed to export template", append(s.summaryLogFields(template.Summary()), dur)...)
|
||||
s.logger.Info("exported template", append(s.summaryLogFields(template.Summary()), dur)...)
|
||||
}(time.Now())
|
||||
return s.next.Export(ctx, opts...)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue