Merge remote-tracking branch 'origin/main' into ds-apiserver-with-configs
This commit is contained in:
@@ -2,6 +2,7 @@ package dashboards
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/infra/slugify"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tests/apis"
|
||||
@@ -112,74 +114,28 @@ func runDashboardTest(t *testing.T, helper *apis.K8sTestHelper, gvr schema.Group
|
||||
|
||||
func TestIntegrationDashboardsAppV0Alpha1(t *testing.T) {
|
||||
gvr := schema.GroupVersionResource{
|
||||
Group: dashboardV1.GROUP,
|
||||
Version: dashboardV1.VERSION,
|
||||
Group: dashboardV0.GROUP,
|
||||
Version: dashboardV0.VERSION,
|
||||
Resource: "dashboards",
|
||||
}
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
t.Run("v0alpha1 with dual writer mode 0", func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: 0,
|
||||
modes := []rest.DualWriterMode{rest.Mode0, rest.Mode1, rest.Mode2, rest.Mode3, rest.Mode4, rest.Mode5}
|
||||
for _, mode := range modes {
|
||||
t.Run(fmt.Sprintf("v0alpha1 with dual writer mode %d", mode), func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: mode,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
|
||||
t.Run("v0alpha1 with dual writer mode 1", func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
|
||||
t.Run("v0alpha1 with dual writer mode 2", func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: 2,
|
||||
},
|
||||
},
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
|
||||
t.Run("v0alpha1 with dual writer mode 3", func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: 3,
|
||||
},
|
||||
},
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
|
||||
t.Run("v0alpha1 with dual writer mode 4", func(t *testing.T) {
|
||||
t.Skip("skipping test because of authorizer issue")
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: 4,
|
||||
},
|
||||
},
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationDashboardsAppV1(t *testing.T) {
|
||||
@@ -192,66 +148,46 @@ func TestIntegrationDashboardsAppV1(t *testing.T) {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
t.Run("v1 with dual writer mode 0", func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: 0,
|
||||
modes := []rest.DualWriterMode{rest.Mode0, rest.Mode1, rest.Mode2, rest.Mode3, rest.Mode4, rest.Mode5}
|
||||
for _, mode := range modes {
|
||||
t.Run(fmt.Sprintf("v1beta1 with dual writer mode %d", mode), func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: mode,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("v1 with dual writer mode 1", func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
func TestIntegrationDashboardsAppV2(t *testing.T) {
|
||||
gvr := schema.GroupVersionResource{
|
||||
Group: dashboardV2.GROUP,
|
||||
Version: dashboardV2.VERSION,
|
||||
Resource: "dashboards",
|
||||
}
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
t.Run("v1 with dual writer mode 2", func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: 2,
|
||||
modes := []rest.DualWriterMode{rest.Mode0, rest.Mode1, rest.Mode2, rest.Mode3, rest.Mode4, rest.Mode5}
|
||||
for _, mode := range modes {
|
||||
t.Run(fmt.Sprintf("v1beta1 with dual writer mode %d", mode), func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: mode,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
|
||||
t.Run("v1 with dual writer mode 3", func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: 3,
|
||||
},
|
||||
},
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
|
||||
t.Run("v1 with dual writer mode 4", func(t *testing.T) {
|
||||
t.Skip("skipping test because of authorizer issue")
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
DualWriterMode: 4,
|
||||
},
|
||||
},
|
||||
})
|
||||
runDashboardTest(t, helper, gvr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationLegacySupport(t *testing.T) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
@@ -1026,7 +1027,10 @@ func TestIntegrationFoldersGetAPIEndpointK8S(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
t.Skip("not working yet")
|
||||
|
||||
if !db.IsTestDbSQLite() {
|
||||
t.Skip("test only on sqlite for now")
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
description string
|
||||
|
||||
@@ -2303,6 +2303,7 @@
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"description": "Spec is the spec of the Check",
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
@@ -2389,6 +2390,33 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"com.github.grafana.grafana.apps.advisor.pkg.apis.advisor.v0alpha1.CheckReport": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"count",
|
||||
"failures"
|
||||
],
|
||||
"properties": {
|
||||
"count": {
|
||||
"description": "Number of elements analyzed",
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"default": 0
|
||||
},
|
||||
"failures": {
|
||||
"description": "List of failures",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.advisor.pkg.apis.advisor.v0alpha1.CheckReportFailure"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.apps.advisor.pkg.apis.advisor.v0alpha1.CheckReportFailure": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -2479,7 +2507,7 @@
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.advisor.pkg.apis.advisor.v0alpha1.CheckV0alpha1StatusReport"
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.advisor.pkg.apis.advisor.v0alpha1.CheckReport"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2510,6 +2538,7 @@
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"description": "Spec is the spec of the CheckType",
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
@@ -2682,33 +2711,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.apps.advisor.pkg.apis.advisor.v0alpha1.CheckV0alpha1StatusReport": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"count",
|
||||
"failures"
|
||||
],
|
||||
"properties": {
|
||||
"count": {
|
||||
"description": "Number of elements analyzed",
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"default": 0
|
||||
},
|
||||
"failures": {
|
||||
"description": "List of failures",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.advisor.pkg.apis.advisor.v0alpha1.CheckReportFailure"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.apps.advisor.pkg.apis.advisor.v0alpha1.CheckstatusOperatorState": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -2923,11 +2923,16 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"title",
|
||||
"description",
|
||||
"version",
|
||||
"group",
|
||||
"permissions"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"group": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
@@ -3236,11 +3241,16 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"title",
|
||||
"description",
|
||||
"version",
|
||||
"group",
|
||||
"permissions"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"group": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
@@ -3723,11 +3733,16 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"title",
|
||||
"description",
|
||||
"version",
|
||||
"group",
|
||||
"permissions"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"group": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
|
||||
@@ -2,10 +2,7 @@ package provisioning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -13,7 +10,6 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
gh "github.com/google/go-github/v70/github"
|
||||
ghmock "github.com/migueleliasweb/go-github-mock/src/mock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -224,20 +220,8 @@ func runGrafana(t *testing.T, options ...grafanaOption) *provisioningTestHelper
|
||||
}
|
||||
helper := apis.NewK8sTestHelper(t, opts)
|
||||
|
||||
helper.GetEnv().GitHubFactory.Client = ghmock.NewMockedHTTPClient(
|
||||
ghmock.WithRequestMatchHandler(ghmock.GetUser, ghAlwaysWrite(t, &gh.User{})),
|
||||
ghmock.WithRequestMatchHandler(ghmock.GetReposHooksByOwnerByRepo, ghAlwaysWrite(t, []*gh.Hook{})),
|
||||
ghmock.WithRequestMatchHandler(ghmock.PostReposHooksByOwnerByRepo, ghAlwaysWrite(t, &gh.Hook{})),
|
||||
ghmock.WithRequestMatchHandler(ghmock.GetReposByOwnerByRepo, ghAlwaysWrite(t, &gh.Repository{})),
|
||||
ghmock.WithRequestMatchHandler(ghmock.GetReposBranchesByOwnerByRepoByBranch, ghAlwaysWrite(t, &gh.Branch{})),
|
||||
ghmock.WithRequestMatchHandler(ghmock.GetReposGitTreesByOwnerByRepoByTreeSha, ghAlwaysWrite(t, &gh.Tree{})),
|
||||
ghmock.WithRequestMatchHandler(
|
||||
ghmock.DeleteReposHooksByOwnerByRepoByHookId,
|
||||
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}),
|
||||
),
|
||||
)
|
||||
// FIXME: keeping this line here to keep the dependency around until we have tests which use this again.
|
||||
helper.GetEnv().GitHubFactory.Client = ghmock.NewMockedHTTPClient()
|
||||
|
||||
repositories := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: helper.Org1.Admin,
|
||||
@@ -310,33 +294,6 @@ func runGrafana(t *testing.T, options ...grafanaOption) *provisioningTestHelper
|
||||
}
|
||||
}
|
||||
|
||||
func ghAlwaysWrite(t *testing.T, body any) http.HandlerFunc {
|
||||
marshalled := ghmock.MustMarshal(body)
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
_, err := w.Write(marshalled)
|
||||
require.NoError(t, err, "failed to write body in mock")
|
||||
})
|
||||
}
|
||||
|
||||
func ghHandleTree(t *testing.T, refs map[string][]*gh.TreeEntry) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
sha := r.URL.Path[strings.LastIndex(r.URL.Path, "/")+1:]
|
||||
require.NotEmpty(t, sha, "sha path parameter was missing?")
|
||||
|
||||
entries := refs[sha]
|
||||
require.NotNil(t, entries, "no entries for sha %s", sha)
|
||||
|
||||
tree := &gh.Tree{
|
||||
SHA: gh.Ptr(sha),
|
||||
Truncated: gh.Ptr(false),
|
||||
Entries: entries,
|
||||
}
|
||||
|
||||
_, err := w.Write(ghmock.MustMarshal(tree))
|
||||
require.NoError(t, err, "failed to write body in mock")
|
||||
})
|
||||
}
|
||||
|
||||
func mustNestedString(obj map[string]interface{}, fields ...string) string {
|
||||
v, _, err := unstructured.NestedString(obj, fields...)
|
||||
if err != nil {
|
||||
@@ -350,15 +307,6 @@ func asJSON(obj any) []byte {
|
||||
return jj
|
||||
}
|
||||
|
||||
func treeEntryDir(dirName string, sha string) *gh.TreeEntry {
|
||||
return &gh.TreeEntry{
|
||||
SHA: gh.Ptr(sha),
|
||||
Path: gh.Ptr(dirName),
|
||||
Type: gh.Ptr("tree"),
|
||||
Mode: gh.Ptr("040000"),
|
||||
}
|
||||
}
|
||||
|
||||
func unstructuredToRepository(t *testing.T, obj *unstructured.Unstructured) *provisioning.Repository {
|
||||
bytes, err := obj.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
@@ -369,33 +317,3 @@ func unstructuredToRepository(t *testing.T, obj *unstructured.Unstructured) *pro
|
||||
|
||||
return repo
|
||||
}
|
||||
|
||||
func treeEntry(fpath string, content []byte) *gh.TreeEntry {
|
||||
sha := sha256.Sum256(content)
|
||||
|
||||
return &gh.TreeEntry{
|
||||
SHA: gh.Ptr(hex.EncodeToString(sha[:])),
|
||||
Path: gh.Ptr(fpath),
|
||||
Size: gh.Ptr(len(content)),
|
||||
Type: gh.Ptr("blob"),
|
||||
Mode: gh.Ptr("100644"),
|
||||
Content: gh.Ptr(string(content)),
|
||||
}
|
||||
}
|
||||
|
||||
func repoContent(fpath string, content []byte) *gh.RepositoryContent {
|
||||
sha := sha256.Sum256(content)
|
||||
typ := "blob"
|
||||
if strings.HasSuffix(fpath, "/") {
|
||||
typ = "tree"
|
||||
}
|
||||
|
||||
return &gh.RepositoryContent{
|
||||
SHA: gh.Ptr(hex.EncodeToString(sha[:])),
|
||||
Name: gh.Ptr(path.Base(fpath)),
|
||||
Path: &fpath,
|
||||
Size: gh.Ptr(len(content)),
|
||||
Type: &typ,
|
||||
Content: gh.Ptr(string(content)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,14 @@ package provisioning
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
gh "github.com/google/go-github/v70/github"
|
||||
ghmock "github.com/migueleliasweb/go-github-mock/src/mock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -173,7 +171,7 @@ func TestIntegrationProvisioning_FailInvalidSchema(t *testing.T) {
|
||||
}, time.Second*10, time.Millisecond*10, "Expected to be able to start a sync job")
|
||||
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
//helper.TriggerJobProcessing(t)
|
||||
// helper.TriggerJobProcessing(t)
|
||||
result, err := helper.Repositories.Resource.Get(ctx, repo, metav1.GetOptions{},
|
||||
"jobs", string(jobObj.GetUID()))
|
||||
|
||||
@@ -211,50 +209,25 @@ func TestIntegrationProvisioning_CreatingGitHubRepository(t *testing.T) {
|
||||
helper := runGrafana(t)
|
||||
ctx := context.Background()
|
||||
|
||||
helper.GetEnv().GitHubFactory.Client = ghmock.NewMockedHTTPClient(
|
||||
ghmock.WithRequestMatchHandler(ghmock.GetUser, ghAlwaysWrite(t, &gh.User{Name: gh.Ptr("github-user")})),
|
||||
ghmock.WithRequestMatchHandler(ghmock.GetReposHooksByOwnerByRepo, ghAlwaysWrite(t, []*gh.Hook{})),
|
||||
ghmock.WithRequestMatchHandler(ghmock.PostReposHooksByOwnerByRepo, ghAlwaysWrite(t, &gh.Hook{ID: gh.Ptr(int64(123))})),
|
||||
ghmock.WithRequestMatchHandler(ghmock.GetReposByOwnerByRepo, ghAlwaysWrite(t, &gh.Repository{ID: gh.Ptr(int64(234))})),
|
||||
ghmock.WithRequestMatchHandler(
|
||||
ghmock.GetReposBranchesByOwnerByRepoByBranch,
|
||||
ghAlwaysWrite(t, &gh.Branch{
|
||||
Name: gh.Ptr("main"),
|
||||
Commit: &gh.RepositoryCommit{SHA: gh.Ptr("deadbeef")},
|
||||
}),
|
||||
),
|
||||
ghmock.WithRequestMatchHandler(ghmock.GetReposGitTreesByOwnerByRepoByTreeSha,
|
||||
ghHandleTree(t, map[string][]*gh.TreeEntry{
|
||||
"deadbeef": {
|
||||
treeEntryDir("grafana", "subtree"),
|
||||
},
|
||||
"subtree": {
|
||||
treeEntry("dashboard.json", helper.LoadFile("testdata/all-panels.json")),
|
||||
treeEntryDir("subdir", "subtree2"),
|
||||
treeEntry("subdir/dashboard2.yaml", helper.LoadFile("testdata/text-options.json")),
|
||||
},
|
||||
})),
|
||||
ghmock.WithRequestMatchHandler(
|
||||
ghmock.GetReposContentsByOwnerByRepoByPath,
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
pathRegex := regexp.MustCompile(`/repos/[^/]+/[^/]+/contents/(.*)`)
|
||||
matches := pathRegex.FindStringSubmatch(r.URL.Path)
|
||||
require.NotNil(t, matches, "no match for contents?")
|
||||
path := matches[1]
|
||||
// FIXME: instead of using an existing GitHub repository, we should create a new one for the tests and a branch
|
||||
// This was the previous structure
|
||||
// ghmock.WithRequestMatchHandler(ghmock.GetReposGitTreesByOwnerByRepoByTreeSha,
|
||||
// ghHandleTree(t, map[string][]*gh.TreeEntry{
|
||||
// "deadbeef": {
|
||||
// treeEntryDir("grafana", "subtree"),
|
||||
// },
|
||||
// "subtree": {
|
||||
// treeEntry("dashboard.json", helper.LoadFile("testdata/all-panels.json")),
|
||||
// treeEntryDir("subdir", "subtree2"),
|
||||
// treeEntry("subdir/dashboard2.yaml", helper.LoadFile("testdata/text-options.json")),
|
||||
// },
|
||||
// })),
|
||||
|
||||
var err error
|
||||
switch path {
|
||||
case "grafana/dashboard.json":
|
||||
_, err = w.Write(ghmock.MustMarshal(repoContent(path, helper.LoadFile("testdata/all-panels.json"))))
|
||||
case "grafana/subdir/dashboard2.yaml":
|
||||
_, err = w.Write(ghmock.MustMarshal(repoContent(path, helper.LoadFile("testdata/text-options.json"))))
|
||||
default:
|
||||
t.Fatalf("got unexpected path: %s", path)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}),
|
||||
),
|
||||
)
|
||||
// FIXME: uncomment these to implement webhook integration tests.
|
||||
// helper.GetEnv().GitHubFactory.Client = ghmock.NewMockedHTTPClient(
|
||||
// ghmock.WithRequestMatchHandler(ghmock.GetReposHooksByOwnerByRepo, ghAlwaysWrite(t, []*gh.Hook{})),
|
||||
// ghmock.WithRequestMatchHandler(ghmock.PostReposHooksByOwnerByRepo, ghAlwaysWrite(t, &gh.Hook{ID: gh.Ptr(int64(123))})),
|
||||
// )
|
||||
|
||||
const repo = "github-create-test"
|
||||
_, err := helper.Repositories.Resource.Create(ctx,
|
||||
@@ -279,8 +252,10 @@ func TestIntegrationProvisioning_CreatingGitHubRepository(t *testing.T) {
|
||||
for _, v := range found.Items {
|
||||
names = append(names, v.GetName())
|
||||
}
|
||||
assert.Contains(t, names, "n1jR8vnnz", "should contain dashboard.json's contents")
|
||||
assert.Contains(t, names, "WZ7AhQiVz", "should contain dashboard2.yaml's contents")
|
||||
require.Len(t, names, 3, "should have three dashboards")
|
||||
assert.Contains(t, names, "adg5vbj", "should contain dashboard.json's contents")
|
||||
assert.Contains(t, names, "admfz74", "should contain dashboard2.yaml's contents")
|
||||
assert.Contains(t, names, "adn5mxb", "should contain dashboard2.yaml's contents")
|
||||
|
||||
err = helper.Repositories.Resource.Delete(ctx, repo, metav1.DeleteOptions{})
|
||||
require.NoError(t, err, "should delete values")
|
||||
@@ -651,3 +626,128 @@ func TestProvisioning_ExportUnifiedToRepository(t *testing.T) {
|
||||
require.Nil(t, obj["status"], "should not have a status element")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationProvisioning_DeleteResources(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
helper := runGrafana(t)
|
||||
ctx := context.Background()
|
||||
|
||||
const repo = "delete-test-repo"
|
||||
localTmp := helper.RenderObject(t, "testdata/local-write.json.tmpl", map[string]any{
|
||||
"Name": repo,
|
||||
"SyncEnabled": true,
|
||||
"SyncTarget": "instance",
|
||||
})
|
||||
_, err := helper.Repositories.Resource.Create(ctx, localTmp, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// create the structure:
|
||||
// dashboard1.json
|
||||
// folder/
|
||||
// dashboard2.json
|
||||
// nested/
|
||||
// dashboard3.json
|
||||
dashboard1 := helper.LoadFile("testdata/all-panels.json")
|
||||
result := helper.AdminREST.Post().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("files", "dashboard1.json").
|
||||
Body(dashboard1).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
Do(ctx)
|
||||
require.NoError(t, result.Error())
|
||||
dashboard2 := helper.LoadFile("testdata/text-options.json")
|
||||
result = helper.AdminREST.Post().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("files", "folder", "dashboard2.json").
|
||||
Body(dashboard2).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
Do(ctx)
|
||||
require.NoError(t, result.Error())
|
||||
dashboard3 := helper.LoadFile("testdata/timeline-demo.json")
|
||||
result = helper.AdminREST.Post().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("files", "folder", "nested", "dashboard3.json").
|
||||
Body(dashboard3).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
Do(ctx)
|
||||
require.NoError(t, result.Error())
|
||||
|
||||
// make sure we don't fail when there is a .keep file in a folder
|
||||
helper.CopyToProvisioningPath(t, "testdata/.keep", "folder/nested/.keep")
|
||||
|
||||
dashboards, err := helper.DashboardsV1.Resource.List(ctx, metav1.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 3, len(dashboards.Items))
|
||||
|
||||
folders, err := helper.Folders.Resource.List(ctx, metav1.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(folders.Items))
|
||||
|
||||
t.Run("delete individual dashboard file, should delete from repo and grafana", func(t *testing.T) {
|
||||
result := helper.AdminREST.Delete().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("files", "dashboard1.json").
|
||||
Do(ctx)
|
||||
require.NoError(t, result.Error())
|
||||
_, err = helper.Repositories.Resource.Get(ctx, repo, metav1.GetOptions{}, "files", "dashboard1.json")
|
||||
require.Error(t, err)
|
||||
dashboards, err = helper.DashboardsV1.Resource.List(ctx, metav1.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(dashboards.Items))
|
||||
})
|
||||
|
||||
t.Run("delete folder, should delete from repo and grafana all nested resources too", func(t *testing.T) {
|
||||
// need to delete directly through the url, because the k8s client doesn't support `/` in a subresource
|
||||
// but that is needed by gitsync to know that it is a folder
|
||||
addr := helper.GetEnv().Server.HTTPServer.Listener.Addr().String()
|
||||
url := fmt.Sprintf("http://admin:admin@%s/apis/provisioning.grafana.app/v0alpha1/namespaces/default/repositories/%s/files/folder/", addr, repo)
|
||||
req, err := http.NewRequest(http.MethodDelete, url, nil)
|
||||
require.NoError(t, err)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
// nolint:errcheck
|
||||
defer resp.Body.Close()
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
// should be deleted from the repo
|
||||
_, err = helper.Repositories.Resource.Get(ctx, repo, metav1.GetOptions{}, "files", "folder")
|
||||
require.Error(t, err)
|
||||
_, err = helper.Repositories.Resource.Get(ctx, repo, metav1.GetOptions{}, "files", "folder", "dashboard2.json")
|
||||
require.Error(t, err)
|
||||
_, err = helper.Repositories.Resource.Get(ctx, repo, metav1.GetOptions{}, "files", "folder", "nested")
|
||||
require.Error(t, err)
|
||||
_, err = helper.Repositories.Resource.Get(ctx, repo, metav1.GetOptions{}, "files", "folder", "nested", "dashboard3.json")
|
||||
require.Error(t, err)
|
||||
|
||||
// all should be deleted from grafana
|
||||
for _, d := range dashboards.Items {
|
||||
_, err = helper.DashboardsV1.Resource.Get(ctx, d.GetName(), metav1.GetOptions{})
|
||||
require.Error(t, err)
|
||||
}
|
||||
for _, f := range folders.Items {
|
||||
_, err = helper.Folders.Resource.Get(ctx, f.GetName(), metav1.GetOptions{})
|
||||
require.Error(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("deleting a non-existent file should fail", func(t *testing.T) {
|
||||
result := helper.AdminREST.Delete().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("files", "non-existent.json").
|
||||
Do(ctx)
|
||||
require.Error(t, result.Error())
|
||||
})
|
||||
}
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
# This file ensures the folder/nested directory is tracked in version control
|
||||
@@ -9,10 +9,10 @@
|
||||
"description": "{{ or .Description .Name "Load grafana dashboard from fake repository" }}",
|
||||
"type": "github",
|
||||
"github": {
|
||||
"url": "{{ or .URL "https://github.com/grafana/git-ui-sync-demo" }}",
|
||||
"branch": "{{ or .Branch "dummy" }}",
|
||||
"url": "{{ or .URL "https://github.com/grafana/grafana-git-sync-demo" }}",
|
||||
"branch": "{{ or .Branch "integration-test" }}",
|
||||
"generateDashboardPreviews": {{ if .GenerateDashboardPreviews }} true {{ else }} false {{ end }},
|
||||
"token": "{{ or .Token "github_pat_dummy" }}",
|
||||
"token": "{{ or .Token "" }}",
|
||||
"path": "{{ or .Path "grafana/" }}"
|
||||
},
|
||||
"sync": {
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../../../devenv/dev-dashboards/panel-timeline/timeline-demo.json
|
||||
Reference in New Issue
Block a user