Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(pkger): add ability to export resources by name from cli #19457

Merged
merged 22 commits into from
Sep 23, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat(pkger): add ability to export dashboards by name
  • Loading branch information
glinton committed Aug 27, 2020
commit 4e1bc7ceb2cc8cfa3b3c72869741df25672753fa
74 changes: 51 additions & 23 deletions cmd/influx/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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, "telegra-namef-configs", "", "List of telegraf config names comma separated")
cmd.Flags().StringVar(&b.exportOpts.variableNames, "variable-names", "", "List of variable names comma separated")

return cmd
}
Expand All @@ -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())
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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
}

Expand All @@ -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
}

Expand Down
4 changes: 3 additions & 1 deletion kv/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hoping this is kosher, it follows the pattern of labels

func filterLabelsFn(filter influxdb.LabelFilter) func(l *influxdb.Label) bool {
	return func(label *influxdb.Label) bool {
		return (filter.Name == "" || (strings.EqualFold(filter.Name, label.Name))) &&
			((filter.OrgID == nil) || (filter.OrgID != nil && *filter.OrgID == label.OrgID))
	}
}

}
}

// FindDashboards retrives all dashboards that match an arbitrary dashboard filter.
Expand Down
49 changes: 34 additions & 15 deletions pkger/clone_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you know if any frontend code depends on this? changing how it handles empty might require changes on the javascript side.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not certain the relationship, but it is required to be omitted when exporting by name (specifying only name in the payload). i suppose the swagger should be updated to note that it is only required when no name is specified and optional if it is specified. ...i don't love how if both are specified, the resource is found by the id and the exported result is renamed to the name provided.

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
Expand All @@ -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
}
Expand Down Expand Up @@ -135,6 +135,7 @@ func (ex *resourceExporter) Export(ctx context.Context, resourcesToClone []Resou
// 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 {
// todo: how did this compile, i just added Name to the type.
iName, jName := resourcesToClone[i].Name, resourcesToClone[j].Name
iKind, jKind := resourcesToClone[i].Kind, resourcesToClone[j].Kind

Expand Down Expand Up @@ -171,13 +172,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)

Expand All @@ -203,6 +201,7 @@ func (ex *resourceExporter) resourceCloneToKind(ctx context.Context, r ResourceT
metaName = ex.uniqName()
}

fmt.Println("STACK Resourcing", r.Kind, r.Name, r.ID, object.Name())
stackResource := StackResource{
APIVersion: APIVersion,
ID: r.ID,
Expand All @@ -220,8 +219,6 @@ 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)
Expand All @@ -238,11 +235,29 @@ func (ex *resourceExporter) resourceCloneToKind(ctx context.Context, r ResourceT
}
mapResource(ch.GetOrgID(), uniqByNameResID, KindCheck, CheckToObject(r.Name, ch))
case r.Kind.is(KindDashboard):
dash, err := findDashboardByIDFull(ctx, ex.dashSVC, r.ID)
filter := influxdb.DashboardFilter{}
if r.ID != influxdb.ID(0) {
filter.IDs = []*influxdb.ID{&r.ID}
}
dashs, i, err := ex.dashSVC.FindDashboards(ctx, filter, influxdb.DefaultDashboardFindOptions)
if err != nil {
return err
}
mapResource(dash.OrganizationID, dash.ID, KindDashboard, DashboardToObject(r.Name, *dash))
if i < 1 {
return errors.New("no dashboards found")
}

for _, dash := range dashs {
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))
}
case r.Kind.is(KindLabel):
l, err := ex.labelSVC.FindLabelByID(ctx, r.ID)
if err != nil {
Expand Down Expand Up @@ -319,6 +334,10 @@ func (ex *resourceExporter) resourceCloneAssociationsGen(ctx context.Context, la
return nil, shouldSkip, nil
}

if len(r.Name) > 0 {
return nil, false, nil
}

labels, err := ex.labelSVC.FindResourceLabels(ctx, influxdb.LabelMappingFilter{
ResourceID: r.ID,
ResourceType: r.Kind.ResourceType(),
Expand Down Expand Up @@ -355,7 +374,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{
Expand Down
Loading