diff --git a/internal/export/dashboards.go b/internal/export/dashboards.go index 508daa98f..500f6dec5 100644 --- a/internal/export/dashboards.go +++ b/internal/export/dashboards.go @@ -59,6 +59,7 @@ func applyTransformations(ctx *transformationContext, objects []common.MapStr) ( decodeObject, stripObjectProperties, standardizeObjectProperties, + removeFleetManagedTags, standardizeObjectID). transform(objects) } diff --git a/internal/export/test/elastic_package_registry.dashboard.json-expected.json b/internal/export/test/elastic_package_registry.dashboard.json-expected.json new file mode 100644 index 000000000..63bcceafd --- /dev/null +++ b/internal/export/test/elastic_package_registry.dashboard.json-expected.json @@ -0,0 +1,54 @@ +{ + "attributes": { + "description": "Overview Elastic Package Registry service", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "optionsJSON": { + "hidePanelTitles": false, + "syncColors": false, + "syncCursor": true, + "syncTooltips": false, + "useMargins": true + }, + "refreshInterval": { + "pause": false, + "value": 60000 + }, + "timeFrom": "now-15m", + "timeRestore": true, + "timeTo": "now", + "title": "[EPR Metrics] Overview", + "version": 1 + }, + "coreMigrationVersion": "8.7.0", + "created_at": "2023-04-04T10:31:18.629Z", + "id": "elastic_package_registry-313c2700-099b-11ed-91b6-3b1f9c2b2771", + "migrationVersion": { + "dashboard": "8.7.0" + }, + "references": [ + { + "id": "metrics-*", + "name": "2b137c7a-b65d-40c1-8ded-f1f0b3a467f1:indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "metrics-*", + "name": "2b137c7a-b65d-40c1-8ded-f1f0b3a467f1:indexpattern-datasource-layer-f29a1076-32b0-4d8b-8205-f1cbe358a72a", + "type": "index-pattern" + }, + { + "id": "elastic_package_registry-54252400-d309-11ed-97de-b7062e02194f", + "name": "tag-ref-54252400-d309-11ed-97de-b7062e02194f", + "type": "tag" + } + ], + "type": "dashboard" +} \ No newline at end of file diff --git a/internal/export/testdata/elastic_package_registry.dashboard.json b/internal/export/testdata/elastic_package_registry.dashboard.json new file mode 100644 index 000000000..7ee225d00 --- /dev/null +++ b/internal/export/testdata/elastic_package_registry.dashboard.json @@ -0,0 +1,74 @@ +{ + "attributes": { + "description": "Overview Elastic Package Registry service", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "optionsJSON": { + "hidePanelTitles": false, + "syncColors": false, + "syncCursor": true, + "syncTooltips": false, + "useMargins": true + }, + "refreshInterval": { + "pause": false, + "value": 60000 + }, + "timeFrom": "now-15m", + "timeRestore": true, + "timeTo": "now", + "title": "[EPR Metrics] Overview", + "version": 1 + }, + "coreMigrationVersion": "8.7.0", + "created_at": "2023-04-04T10:31:18.629Z", + "id": "elastic_package_registry-313c2700-099b-11ed-91b6-3b1f9c2b2771", + "migrationVersion": { + "dashboard": "8.7.0" + }, + "references": [ + { + "id": "metrics-*", + "name": "2b137c7a-b65d-40c1-8ded-f1f0b3a467f1:indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "metrics-*", + "name": "2b137c7a-b65d-40c1-8ded-f1f0b3a467f1:indexpattern-datasource-layer-f29a1076-32b0-4d8b-8205-f1cbe358a72a", + "type": "index-pattern" + }, + { + "id": "elastic_package_registry-fleet-managed-default", + "name": "tag-ref-elastic_package_registry-fleet-managed-default", + "type": "tag" + }, + { + "id": "elastic_package_registry-fleet-pkg-elastic_package_registry-default", + "name": "tag-ref-elastic_package_registry-fleet-pkg-elastic_package_registry-default", + "type": "tag" + }, + { + "id": "elastic_package_registry-fleet-managed-default", + "name": "tag-ref-fleet-managed-default", + "type": "tag" + }, + { + "id": "elastic_package_registry-fleet-pkg-elastic_package_registry-default", + "name": "tag-ref-fleet-pkg-elastic_package_registry-default", + "type": "tag" + }, + { + "id": "elastic_package_registry-54252400-d309-11ed-97de-b7062e02194f", + "name": "tag-ref-54252400-d309-11ed-97de-b7062e02194f", + "type": "tag" + } + ], + "type": "dashboard" +} \ No newline at end of file diff --git a/internal/export/testdata/elastic_package_registry.random_tag.json b/internal/export/testdata/elastic_package_registry.random_tag.json new file mode 100644 index 000000000..8924148f8 --- /dev/null +++ b/internal/export/testdata/elastic_package_registry.random_tag.json @@ -0,0 +1,15 @@ +{ + "attributes": { + "color": "#207038", + "description": "", + "name": "foo" + }, + "coreMigrationVersion": "8.7.0", + "created_at": "2023-04-04T16:54:13.824Z", + "id": "elastic_package_registry-54252400-d309-11ed-97de-b7062e02194f", + "migrationVersion": { + "tag": "8.0.0" + }, + "references": [], + "type": "tag" +} \ No newline at end of file diff --git a/internal/export/testdata/elastic_package_registry.tag_managed_by_fleet.json b/internal/export/testdata/elastic_package_registry.tag_managed_by_fleet.json new file mode 100644 index 000000000..90e01756b --- /dev/null +++ b/internal/export/testdata/elastic_package_registry.tag_managed_by_fleet.json @@ -0,0 +1,15 @@ +{ + "attributes": { + "color": "#FFFFFF", + "description": "", + "name": "Managed" + }, + "coreMigrationVersion": "8.7.0", + "created_at": "2023-04-04T16:50:46.882Z", + "id": "elastic_package_registry-fleet-managed-default", + "migrationVersion": { + "tag": "8.0.0" + }, + "references": [], + "type": "tag" +} \ No newline at end of file diff --git a/internal/export/transform_remove_fleet_managed_tags.go b/internal/export/transform_remove_fleet_managed_tags.go new file mode 100644 index 000000000..352f545a0 --- /dev/null +++ b/internal/export/transform_remove_fleet_managed_tags.go @@ -0,0 +1,113 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package export + +import ( + "fmt" + + "github.com/elastic/elastic-package/internal/common" +) + +func removeFleetManagedTags(ctx *transformationContext, object common.MapStr) (common.MapStr, error) { + aType, err := object.GetValue("type") + if err != nil { + return nil, fmt.Errorf("failed to read type field: %w", err) + } + + if aType == "tag" { + return removeTagObjects(ctx, object) + } + + return removeTagReferences(ctx, object) +} + +func removeTagReferences(ctx *transformationContext, object common.MapStr) (common.MapStr, error) { + references, err := object.GetValue("references") + if err == common.ErrKeyNotFound { + return object, nil + } + + if err != nil { + return nil, fmt.Errorf("failed to read references field: %w", err) + } + + newReferences, err := filterOutFleetManagedTags(ctx, references.([]interface{})) + if err != nil { + return nil, err + } + + _, err = object.Put("references", newReferences) + if err != nil { + return nil, fmt.Errorf("can't update references: %w", err) + } + + return object, nil +} + +func removeTagObjects(ctx *transformationContext, object common.MapStr) (common.MapStr, error) { + aId, err := object.GetValue("id") + if err == common.ErrKeyNotFound { + return object, nil + } + + if err != nil { + return nil, fmt.Errorf("failed to read id field: %w", err) + } + + aIdString, ok := aId.(string) + if !ok { + return nil, fmt.Errorf("failed to assert id as a string: %v", aId) + } + + if isTagFleetManaged(aIdString, ctx.packageName) { + return nil, nil + } + return object, nil +} + +func isTagFleetManaged(aId, packageName string) bool { + var empty interface{} + fleetManagedTags := map[string]interface{}{ + fmt.Sprintf("fleet-pkg-%s-default", packageName): empty, + "fleet-managed-default": empty, + fmt.Sprintf("%s-fleet-pkg-%s-default", packageName, packageName): empty, + fmt.Sprintf("%s-fleet-managed-default", packageName): empty, + } + + _, ok := fleetManagedTags[aId] + return ok +} + +func filterOutFleetManagedTags(ctx *transformationContext, references []interface{}) ([]interface{}, error) { + newReferences := make([]interface{}, 0) + for _, r := range references { + reference := r.(map[string]interface{}) + + aType, ok := reference["type"] + if !ok { + continue + } + if aType != "tag" { + newReferences = append(newReferences, r) + continue + } + + aId, ok := reference["id"] + if !ok { + newReferences = append(newReferences, r) + continue + } + + aIdString, ok := aId.(string) + if !ok { + return nil, fmt.Errorf("failed to assert id as a string: %v", aId) + } + if isTagFleetManaged(aIdString, ctx.packageName) { + continue + } + newReferences = append(newReferences, r) + } + return newReferences, nil +} diff --git a/internal/export/transform_remove_fleet_managed_tags_test.go b/internal/export/transform_remove_fleet_managed_tags_test.go new file mode 100644 index 000000000..4661c512c --- /dev/null +++ b/internal/export/transform_remove_fleet_managed_tags_test.go @@ -0,0 +1,87 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package export + +import ( + "encoding/json" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/elastic/elastic-package/internal/common" +) + +func TestRemoveFleetTagsDashboard(t *testing.T) { + b, err := os.ReadFile("./testdata/elastic_package_registry.dashboard.json") + require.NoError(t, err) + + var given common.MapStr + err = json.Unmarshal(b, &given) + require.NoError(t, err) + + ctx := &transformationContext{ + packageName: "elastic_package_registry", + } + + result, err := removeFleetManagedTags(ctx, given) + require.NoError(t, err) + + resultJson, err := json.MarshalIndent(&result, "", " ") + require.NoError(t, err) + + expected, err := os.ReadFile("./test/elastic_package_registry.dashboard.json-expected.json") + require.NoError(t, err) + + require.Equal(t, string(expected), string(resultJson)) +} + +func TestRemoveFleetTagsObjects(t *testing.T) { + cases := []struct { + title string + objectFile string + expectedRemoved bool + }{ + { + title: "Tag managed by fleet", + objectFile: "./testdata/elastic_package_registry.tag_managed_by_fleet.json", + expectedRemoved: true, + }, + { + title: "Random tag", + objectFile: "./testdata/elastic_package_registry.random_tag.json", + expectedRemoved: false, + }, + } + + for _, c := range cases { + t.Run(c.title, func(t *testing.T) { + b, err := os.ReadFile(c.objectFile) + require.NoError(t, err) + + var given common.MapStr + err = json.Unmarshal(b, &given) + require.NoError(t, err) + + ctx := &transformationContext{ + packageName: "elastic_package_registry", + } + + result, err := removeFleetManagedTags(ctx, given) + require.NoError(t, err) + + if c.expectedRemoved { + assert.Nil(t, result) + return + } + + resultJson, err := json.MarshalIndent(&result, "", " ") + require.NoError(t, err) + + assert.Equal(t, string(b), string(resultJson)) + }) + } +}