Skip to content

Commit

Permalink
feat: grail filter-segments download
Browse files Browse the repository at this point in the history
  • Loading branch information
jskelin committed Dec 10, 2024
1 parent 6420bbe commit 8763d1a
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 8 deletions.
2 changes: 1 addition & 1 deletion cmd/monaco/download/download_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func GetDownloadCommand(fs afero.Fs, command Command) (cmd *cobra.Command) {
}

if featureflags.Temporary[featureflags.GrailFilterSegment].Enabled() {
cmd.Flags().BoolVar(&f.onlyOpenPipeline, "only-filtersegments", false, "Only download Grail filter-segment configurations, skip all other configuration types")
cmd.Flags().BoolVar(&f.onlyGrailFilterSegments, "only-filtersegments", false, "Only download Grail filter-segment configurations, skip all other configuration types")
}

err := errors.Join(
Expand Down
18 changes: 11 additions & 7 deletions cmd/monaco/download/download_configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/download/classic"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/download/dependency_resolution"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/download/document"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/download/grailfiltersegment"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/download/id_extraction"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/download/openpipeline"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/download/settings"
Expand All @@ -59,6 +60,7 @@ type downloadCmdOptions struct {
onlyAutomation bool
onlyDocuments bool
onlyOpenPipeline bool
onlyGrailFilterSegments bool
}

type auth struct {
Expand Down Expand Up @@ -138,13 +140,14 @@ func (d DefaultCommand) DownloadConfigsBasedOnManifest(fs afero.Fs, cmdOptions d
projectName: cmdOptions.projectName,
forceOverwriteManifest: cmdOptions.forceOverwrite,
},
specificAPIs: cmdOptions.specificAPIs,
specificSchemas: cmdOptions.specificSchemas,
onlyAPIs: cmdOptions.onlyAPIs,
onlySettings: cmdOptions.onlySettings,
onlyAutomation: cmdOptions.onlyAutomation,
onlyDocuments: cmdOptions.onlyDocuments,
onlyOpenPipeline: cmdOptions.onlyOpenPipeline,
specificAPIs: cmdOptions.specificAPIs,
specificSchemas: cmdOptions.specificSchemas,
onlyAPIs: cmdOptions.onlyAPIs,
onlySettings: cmdOptions.onlySettings,
onlyAutomation: cmdOptions.onlyAutomation,
onlyDocuments: cmdOptions.onlyDocuments,
onlyOpenPipeline: cmdOptions.onlyOpenPipeline,
onlyGrailFilterSegment: cmdOptions.onlyGrailFilterSegments,
}

if errs := options.valid(); len(errs) != 0 {
Expand Down Expand Up @@ -248,6 +251,7 @@ var defaultDownloadFn = downloadFn{
bucketDownload: bucket.Download,
documentDownload: document.Download,
openPipelineDownload: openpipeline.Download,
grailFilterSegment: grailfiltersegment.Download,
}

func downloadConfigs(clientSet *client.ClientSet, apisToDownload api.APIs, opts downloadConfigsOptions, fn downloadFn) (project.ConfigsPerType, error) {
Expand Down
2 changes: 2 additions & 0 deletions pkg/client/clientset.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/dynatrace/dynatrace-configuration-as-code-core/clients/automation"
"github.com/dynatrace/dynatrace-configuration-as-code-core/clients/buckets"
"github.com/dynatrace/dynatrace-configuration-as-code-core/clients/documents"
"github.com/dynatrace/dynatrace-configuration-as-code-core/clients/grailfiltersegments"
"github.com/dynatrace/dynatrace-configuration-as-code-core/clients/openpipeline"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/internal/environment"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/internal/log"
Expand Down Expand Up @@ -166,6 +167,7 @@ type OpenPipelineClient interface {
}

type GrailFilterSegmentClient interface {
GetAll(ctx context.Context) ([]grailfiltersegments.Response, error)
}

var DefaultMonacoUserAgent = "Dynatrace Monitoring as Code/" + version.MonitoringAsCode + " " + (runtime.GOOS + " " + runtime.GOARCH)
Expand Down
88 changes: 88 additions & 0 deletions pkg/download/grailfiltersegment/download.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* @license
* Copyright 2024 Dynatrace LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package grailfiltersegment

import (
"context"
"fmt"

"github.com/dynatrace/dynatrace-configuration-as-code-core/clients/openpipeline"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/internal/log"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/internal/log/field"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/client"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/config"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/config/coordinate"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/config/template"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/download/internal/templatetools"
project "github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/project/v2"
)

func Download(client client.GrailFilterSegmentClient, projectName string) (project.ConfigsPerType, error) {
result := project.ConfigsPerType{}

dtos, err := client.GetAll(context.TODO())
if err != nil {
log.WithFields(field.Type(config.GrailFilterSegmentID), field.Error(err)).Error("Failed to fetch the list of existing filter-segments: %v", err)
return nil, nil
}

var configs []config.Config
for _, dto := range dtos {
c, err := createConfig(projectName, dto)
if err != nil {
log.WithFields(field.Type(config.GrailFilterSegmentID), field.Error(err)).Error("Failed to convert config of type '%s': %v", config.GrailFilterSegmentID, err)
continue
}
configs = append(configs, c)
}
result[string(config.GrailFilterSegmentID)] = configs

return result, nil
}

func createConfig(projectName string, response openpipeline.Response) (config.Config, error) {
jsonObj, err := templatetools.NewJSONObject(response.Data)
if err != nil {
return config.Config{}, fmt.Errorf("failed to unmarshal payload: %w", err)
}

id, ok := jsonObj.Get("uid").(string)
if !ok {
return config.Config{}, fmt.Errorf("failed to extract id as string from payload")
}

jsonObj.Delete("uid")
jsonObj.Delete("version")
jsonObj.Delete("externalId")

jsonRaw, err := jsonObj.ToJSON(true)
if err != nil {
return config.Config{}, fmt.Errorf("failed to marshal payload: %w", err)
}

return config.Config{
Template: template.NewInMemoryTemplate(id, string(jsonRaw)),
Coordinate: coordinate.Coordinate{
Project: projectName,
Type: string(config.GrailFilterSegmentID),
ConfigId: id,
},
OriginObjectId: id,
Type: config.GrailFilterSegment{},
Parameters: make(config.Parameters),
}, nil
}
105 changes: 105 additions & 0 deletions pkg/download/grailfiltersegment/download_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* @license
* Copyright 2024 Dynatrace LLC
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package grailfiltersegment_test

import (
"net/http"
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/dynatrace/dynatrace-configuration-as-code-core/api/rest"
coreLib "github.com/dynatrace/dynatrace-configuration-as-code-core/clients/grailfiltersegments"
"github.com/dynatrace/dynatrace-configuration-as-code-core/testutils"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/internal/featureflags"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/config"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/download/grailfiltersegment"
)

func TestDownloader_Download(t *testing.T) {
t.Run("download grail filter-segments works", func(t *testing.T) {
t.Setenv(featureflags.Temporary[featureflags.GrailFilterSegment].EnvName(), "true")
server := testutils.NewHTTPTestServer(t, []testutils.ResponseDef{
{
GET: func(t *testing.T, req *http.Request) testutils.Response {
data, err := os.ReadFile("./testdata/listResponse.json")
assert.NoError(t, err)

return testutils.Response{
ResponseCode: http.StatusOK,
ResponseBody: string(data),
}
},
ValidateRequest: func(t *testing.T, request *http.Request) {
assert.Equal(t, "/platform/storage/filter-segments/v1/filter-segments:lean", request.URL.Path)
assert.Equal(t, "add-fields=EXTERNALID", request.URL.RawQuery)
},
},
{
GET: func(t *testing.T, req *http.Request) testutils.Response {
data, err := os.ReadFile("./testdata/uid_1_getResponse.json")
assert.NoError(t, err)

return testutils.Response{
ResponseCode: http.StatusOK,
ResponseBody: string(data),
}
},
ValidateRequest: func(t *testing.T, request *http.Request) {
assert.Equal(t, "/platform/storage/filter-segments/v1/filter-segments/uid_1", request.URL.Path)
assert.Equal(t, "add-fields=INCLUDES&add-fields=VARIABLES&add-fields=EXTERNALID&add-fields=RESOURCECONTEXT", request.URL.RawQuery)
},
},
{
GET: func(t *testing.T, req *http.Request) testutils.Response {
data, err := os.ReadFile("./testdata/uid_2_getResponse.json")
assert.NoError(t, err)

return testutils.Response{
ResponseCode: http.StatusOK,
ResponseBody: string(data),
}
},
ValidateRequest: func(t *testing.T, request *http.Request) {
assert.Equal(t, "/platform/storage/filter-segments/v1/filter-segments/uid_2", request.URL.Path)
assert.Equal(t, "add-fields=INCLUDES&add-fields=VARIABLES&add-fields=EXTERNALID&add-fields=RESOURCECONTEXT", request.URL.RawQuery)
},
},
})
defer server.Close()

client := coreLib.NewClient(rest.NewClient(server.URL(), server.Client()))
result, err := grailfiltersegment.Download(client, "project")

assert.NoError(t, err)
assert.Len(t, result, 1)

require.Len(t, result[string(config.GrailFilterSegmentID)], 2, "all listed segments should be downloaded")
})

t.Run("no error downloading grail filter segments with faulty client", func(t *testing.T) {
server := testutils.NewHTTPTestServer(t, []testutils.ResponseDef{})
defer server.Close()

client := coreLib.NewClient(rest.NewClient(server.URL(), server.FaultyClient()))
result, err := grailfiltersegment.Download(client, "project")
assert.NoError(t, err)
assert.Empty(t, result)
})
}
14 changes: 14 additions & 0 deletions pkg/download/grailfiltersegment/testdata/listResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"filterSegments": [
{
"uid": "uid_1",
"name": "private_grail_filter_segment",
"isPublic": false
},
{
"uid": "uid_2",
"name": "public_grail_filter_segment",
"isPublic": true
}
]
}
18 changes: 18 additions & 0 deletions pkg/download/grailfiltersegment/testdata/uid_1_getResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"uid": "uid_1",
"name": "private_grail_filter_segment",
"description": "description for private test grail filter segment",
"variables": {
"type": "query",
"value": "fetch logs | limit 1"
},
"isPublic": false,
"owner": "bd62720e-e72a-49c6-aff2-43ca82662bee",
"allowedOperations": [
"READ",
"WRITE",
"DELETE"
],
"includes": [],
"version": 1
}
18 changes: 18 additions & 0 deletions pkg/download/grailfiltersegment/testdata/uid_2_getResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"uid": "uid_2",
"name": "public_grail_filter_segment",
"description": "description for public test grail filter segment",
"variables": {
"type": "query",
"value": "fetch logs | limit 1"
},
"isPublic": true,
"owner": "bd62720e-e72a-49c6-aff2-43ca82662bee",
"allowedOperations": [
"READ",
"WRITE",
"DELETE"
],
"includes": [],
"version": 888
}

0 comments on commit 8763d1a

Please sign in to comment.