From 39a906de4e6a9aa00a572ac1ed057156b04f684c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20L=C3=B3pez=20de=20la=20Franca=20Beltran?= <5459617+joanlopez@users.noreply.github.com> Date: Fri, 13 Oct 2023 13:39:18 +0200 Subject: [PATCH] Deprecate --directory/-d flag in favor of os.Stat (#259) * Deprecate --directory/-d flag in favor of os.Stat * Add tests for grizzly.Pull --- cmd/grr/workflow.go | 4 ++ docs/content/workflows.md | 4 +- pkg/grafana/dashboards_test.go | 5 +- pkg/grafana/datasource_test.go | 5 +- pkg/grafana/folders_test.go | 5 +- .../provisioning/datasources/appdynamics.yaml | 3 +- pkg/grizzly/config.go | 2 +- pkg/grizzly/parsing.go | 10 ++- pkg/grizzly/workflow.go | 26 +++++-- pkg/grizzly/workflow_test.go | 70 +++++++++++++++++++ .../testutil/testutil.go} | 8 +-- 11 files changed, 123 insertions(+), 19 deletions(-) create mode 100644 pkg/grizzly/workflow_test.go rename pkg/{grafana/docker_utils.go => internal/testutil/testutil.go} (77%) diff --git a/cmd/grr/workflow.go b/cmd/grr/workflow.go index 9d879107..c6f0e959 100644 --- a/cmd/grr/workflow.go +++ b/cmd/grr/workflow.go @@ -226,9 +226,13 @@ func providersCmd() *cli.Command { } func initialiseCmd(cmd *cli.Command, opts *grizzly.Opts) *cli.Command { + // Keep the old flags for backwards compatibility cmd.Flags().BoolVarP(&opts.Directory, "directory", "d", false, "treat resource path as a directory") + cmd.Flags().MarkDeprecated("directory", "now it is inferred from the operating system") + cmd.Flags().StringSliceVarP(&opts.Targets, "target", "t", nil, "resources to target") cmd.Flags().StringSliceVarP(&opts.JsonnetPaths, "jpath", "J", getDefaultJsonnetFolders(), "Specify an additional library search dir (right-most wins)") + return initialiseLogging(cmd, &opts.LoggingOpts) } diff --git a/docs/content/workflows.md b/docs/content/workflows.md index 98abe2b9..8fd7d4fe 100644 --- a/docs/content/workflows.md +++ b/docs/content/workflows.md @@ -16,7 +16,7 @@ Grafana instances. To pull dashboards and folders from one instance to another is as simple as: ``` $ export GRAFANA_URL=<...source Grafana URL...> -$ grr pull -d resources -t "Dashboard/*" -t "DashboardFolder/*" +$ grr pull resources -t "Dashboard/*" -t "DashboardFolder/*" ``` This asks Grizzly to pull all resources matching the `/` pattern for dashboards and folders into a directory called `resources`. @@ -28,7 +28,7 @@ dashboards and folders into a directory called `resources`. To push them to a new Grafana instance: ``` $ export GRAFANA_URL=<...destination Grafana URL...> -$ grr apply -d resources +$ grr apply resources ``` ## Jsonnet diff --git a/pkg/grafana/dashboards_test.go b/pkg/grafana/dashboards_test.go index 737fe8db..9db32f9f 100644 --- a/pkg/grafana/dashboards_test.go +++ b/pkg/grafana/dashboards_test.go @@ -6,18 +6,19 @@ import ( "testing" "github.com/grafana/grizzly/pkg/grizzly" + . "github.com/grafana/grizzly/pkg/internal/testutil" "github.com/stretchr/testify/require" ) func TestDashboard(t *testing.T) { - os.Setenv("GRAFANA_URL", getUrl()) + os.Setenv("GRAFANA_URL", GetUrl()) grizzly.ConfigureProviderRegistry( []grizzly.Provider{ &Provider{}, }) - ticker := pingService(getUrl()) + ticker := PingService(GetUrl()) defer ticker.Stop() t.Run("get remote dashboard - success", func(t *testing.T) { diff --git a/pkg/grafana/datasource_test.go b/pkg/grafana/datasource_test.go index d448b43d..9a29add7 100644 --- a/pkg/grafana/datasource_test.go +++ b/pkg/grafana/datasource_test.go @@ -6,18 +6,19 @@ import ( "testing" "github.com/grafana/grizzly/pkg/grizzly" + . "github.com/grafana/grizzly/pkg/internal/testutil" "github.com/stretchr/testify/require" ) func TestDatasources(t *testing.T) { - os.Setenv("GRAFANA_URL", getUrl()) + os.Setenv("GRAFANA_URL", GetUrl()) grizzly.ConfigureProviderRegistry( []grizzly.Provider{ &Provider{}, }) - ticker := pingService(getUrl()) + ticker := PingService(GetUrl()) defer ticker.Stop() t.Run("get remote datasource - success", func(t *testing.T) { diff --git a/pkg/grafana/folders_test.go b/pkg/grafana/folders_test.go index a31be0ec..6d265fe1 100644 --- a/pkg/grafana/folders_test.go +++ b/pkg/grafana/folders_test.go @@ -6,13 +6,14 @@ import ( "testing" "github.com/grafana/grizzly/pkg/grizzly" + . "github.com/grafana/grizzly/pkg/internal/testutil" "github.com/stretchr/testify/require" ) func TestFolders(t *testing.T) { - os.Setenv("GRAFANA_URL", getUrl()) + os.Setenv("GRAFANA_URL", GetUrl()) - ticker := pingService(getUrl()) + ticker := PingService(GetUrl()) defer ticker.Stop() t.Run("get remote folder - success", func(t *testing.T) { diff --git a/pkg/grafana/testdata/provisioning/datasources/appdynamics.yaml b/pkg/grafana/testdata/provisioning/datasources/appdynamics.yaml index d83ed680..3df3dc04 100644 --- a/pkg/grafana/testdata/provisioning/datasources/appdynamics.yaml +++ b/pkg/grafana/testdata/provisioning/datasources/appdynamics.yaml @@ -6,7 +6,8 @@ deleteDatasources: orgId: 1 datasources: - - name: AppDynamics + - uid: 392IktgGk + name: AppDynamics type: dlopes7-appdynamics-datasource access: proxy basicAuth: true diff --git a/pkg/grizzly/config.go b/pkg/grizzly/config.go index 6a1ce61c..ef9ebdae 100644 --- a/pkg/grizzly/config.go +++ b/pkg/grizzly/config.go @@ -8,7 +8,7 @@ type LoggingOpts struct { // Opts contains options for most Grizzly commands type Opts struct { LoggingOpts - Directory bool + Directory bool // Deprecated: now is gathered with os.Stat() JsonnetPaths []string Targets []string } diff --git a/pkg/grizzly/parsing.go b/pkg/grizzly/parsing.go index 8a8f23da..fd5ed9e0 100644 --- a/pkg/grizzly/parsing.go +++ b/pkg/grizzly/parsing.go @@ -20,14 +20,21 @@ import ( ) func Parse(resourcePath string, opts Opts) (Resources, error) { - if !(opts.Directory) { + stat, err := os.Stat(resourcePath) + if err != nil { + return nil, err + } + + if !stat.IsDir() { return ParseFile(opts, resourcePath) } + var resources Resources files, err := FindResourceFiles(resourcePath) if err != nil { return nil, err } + for _, file := range files { r, err := ParseFile(opts, file) if err != nil { @@ -35,6 +42,7 @@ func Parse(resourcePath string, opts Opts) (Resources, error) { } resources = append(resources, r...) } + return resources, nil } diff --git a/pkg/grizzly/workflow.go b/pkg/grizzly/workflow.go index ad1658a1..5bf46f22 100644 --- a/pkg/grizzly/workflow.go +++ b/pkg/grizzly/workflow.go @@ -103,14 +103,20 @@ func ListRemote(opts Opts) error { return w.Flush() } -// Pulls remote resources +// Pull pulls remote resources and stores them in the local file system. +// The given resourcePath must be a directory, where all resources will be stored. func Pull(resourcePath string, opts Opts) error { - log.Infof("Pulling resources from %s", resourcePath) + isFile, err := isFile(resourcePath) + if err != nil { + return err + } - if !(opts.Directory) { - return fmt.Errorf("pull only works with -d option") + if isFile { + return fmt.Errorf("pull must be a directory") } + log.Infof("Pulling resources to %s", resourcePath) + for name, handler := range Registry.Handlers { if !Registry.HandlerMatchesTarget(handler, opts.Targets) { notifier.Info(notifier.SimpleString(handler.Kind()), "skipped") @@ -456,3 +462,15 @@ func Export(exportDir string, resources Resources) error { } return nil } + +func isFile(resourcePath string) (bool, error) { + stat, err := os.Stat(resourcePath) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + + return !stat.IsDir(), nil +} diff --git a/pkg/grizzly/workflow_test.go b/pkg/grizzly/workflow_test.go new file mode 100644 index 00000000..d3168782 --- /dev/null +++ b/pkg/grizzly/workflow_test.go @@ -0,0 +1,70 @@ +package grizzly_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/grafana/grizzly/pkg/grafana" + "github.com/grafana/grizzly/pkg/grizzly" + . "github.com/grafana/grizzly/pkg/internal/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPull(t *testing.T) { + os.Setenv("GRAFANA_URL", GetUrl()) + + grizzly.ConfigureProviderRegistry( + []grizzly.Provider{ + &grafana.Provider{}, + }) + + ticker := PingService(GetUrl()) + defer ticker.Stop() + + opts := grizzly.Opts{ + Targets: []string{ + "Datasource/392IktgGk", + }, + } + + t.Run("with existing file", func(t *testing.T) { + t.Parallel() + + path := filepath.Join(t.TempDir(), filepath.Base(t.Name())) + f, err := os.Create(path) + require.NoError(t, err) + require.NoError(t, f.Close()) + + err = grizzly.Pull(path, opts) + assert.Error(t, err) + assert.ErrorContains(t, err, "pull must be a directory") + }) + + t.Run("with existing folder", func(t *testing.T) { + t.Parallel() + + path := filepath.Join(t.TempDir(), filepath.Base(t.Name())) + err := os.MkdirAll(path, 0755) + require.NoError(t, err) + + err = grizzly.Pull(path, opts) + assert.NoError(t, err) + assert.Equal(t, 1, numOfFiles(path)) + }) + + t.Run("with non-existing folder", func(t *testing.T) { + t.Parallel() + + path := filepath.Join(t.TempDir(), filepath.Base(t.Name())) + err := grizzly.Pull(path, opts) + assert.NoError(t, err) + assert.Equal(t, 1, numOfFiles(path)) + }) +} + +func numOfFiles(path string) int { + files, _ := os.ReadDir(path) + return len(files) +} diff --git a/pkg/grafana/docker_utils.go b/pkg/internal/testutil/testutil.go similarity index 77% rename from pkg/grafana/docker_utils.go rename to pkg/internal/testutil/testutil.go index 7b04ca07..e020c315 100644 --- a/pkg/grafana/docker_utils.go +++ b/pkg/internal/testutil/testutil.go @@ -1,4 +1,4 @@ -package grafana +package testutil import ( "fmt" @@ -7,7 +7,7 @@ import ( "time" ) -func getUrl() string { +func GetUrl() string { if os.Getenv("CI") != "" { return "http://grizzly-grafana:3000/" } else { @@ -15,7 +15,7 @@ func getUrl() string { } } -func pingService(url string) *time.Ticker { +func PingService(url string) *time.Ticker { ticker := time.NewTicker(1 * time.Second) timeoutExceeded := time.After(120 * time.Second) @@ -23,7 +23,7 @@ func pingService(url string) *time.Ticker { for !success { select { case <-timeoutExceeded: - panic("Unable to connect to grizzly-grafana:3000") + panic(fmt.Sprintf("Unable to connect to %s", url)) case <-ticker.C: resp, _ := http.Get(url)