K8s/OpenAPI: Render openapi into a static file (#99561)

This commit is contained in:
Ryan McKinley 2025-01-28 10:30:53 +03:00 committed by GitHub
parent 61c5b4a25e
commit 4e703576b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 7181 additions and 33 deletions

7
.github/CODEOWNERS vendored
View File

@ -593,9 +593,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/AppWrapper.tsx @grafana/frontend-ops
/public/app/partials/ @grafana/grafana-frontend-platform
/scripts/benchmark-access-control.sh @grafana/access-squad
/scripts/check-breaking-changes.sh @grafana/plugins-platform-frontend
/scripts/ci-* @grafana/grafana-developer-enablement-squad
@ -734,6 +731,10 @@ embed.go @grafana/grafana-as-code
/public/app/plugins/*gen.go @grafana/grafana-as-code
/cue.mod/ @grafana/grafana-as-code
# Rendered OpenAPI from app platform
# Eventually each file owned by the right team, OR a structure with the rendered value under /apis/{group}/openapi
/openapi/ @grafana/grafana-app-platform-squad
# GitHub Workflows and Templates
/.github/CODEOWNERS @tolzhabayev
/.github/ISSUE_TEMPLATE/ @torkelo @sympatheticmoose

3
openapi/README.md Normal file
View File

@ -0,0 +1,3 @@
This folder contains a rendered OpenAPI for each group/version
The "real" openapi is generated by the running server, but this is used to build static frontend clients

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,120 @@
package core
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"path/filepath"
"testing"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/version"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/tests/apis"
"github.com/grafana/grafana/pkg/tests/testinfra"
"github.com/grafana/grafana/pkg/tests/testsuite"
)
func TestMain(m *testing.M) {
testsuite.Run(m)
}
func TestIntegrationOpenAPIs(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
check := []schema.GroupVersion{{
Group: "dashboard.grafana.app",
Version: "v0alpha1",
}, {
Group: "folder.grafana.app",
Version: "v0alpha1",
}, {
Group: "peakq.grafana.app",
Version: "v0alpha1",
}}
h := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
AppModeProduction: true,
EnableFeatureToggles: []string{
featuremgmt.FlagKubernetesFoldersServiceV2, // Will be default on by G12
featuremgmt.FlagQueryService, // Query Library
},
})
t.Run("check valid version response", func(t *testing.T) {
disco := h.NewDiscoveryClient()
req := disco.RESTClient().Get().
Prefix("version").
SetHeader("Accept", "application/json")
result := req.Do(context.Background())
require.NoError(t, result.Error())
raw, err := result.Raw()
require.NoError(t, err)
info := apimachineryversion.Info{}
err = json.Unmarshal(raw, &info)
require.NoError(t, err)
// Make sure the gitVersion is parsable
v, err := version.Parse(info.GitVersion)
require.NoError(t, err)
require.Equal(t, info.Major, fmt.Sprintf("%d", v.Major()))
require.Equal(t, info.Minor, fmt.Sprintf("%d", v.Minor()))
})
t.Run("build open", func(t *testing.T) {
// Now write each OpenAPI spec to a static file
dir := filepath.Join("..", "..", "..", "..", "openapi")
for _, gv := range check {
path := fmt.Sprintf("/openapi/v3/apis/%s/%s", gv.Group, gv.Version)
rsp := apis.DoRequest(h, apis.RequestParams{
Method: http.MethodGet,
Path: path,
User: h.Org1.Admin,
}, &apis.AnyResource{})
require.NotNil(t, rsp.Response)
require.Equal(t, 200, rsp.Response.StatusCode, path)
var prettyJSON bytes.Buffer
err := json.Indent(&prettyJSON, rsp.Body, "", " ")
require.NoError(t, err)
pretty := prettyJSON.String()
write := false
fpath := filepath.Join(dir, fmt.Sprintf("%s-%s.json", gv.Group, gv.Version))
// nolint:gosec
// We can ignore the gosec G304 warning since this is a test and the function is only called with explicit paths
body, err := os.ReadFile(fpath)
if err == nil {
if !assert.JSONEq(t, string(body), pretty) {
t.Logf("openapi spec has changed: %s", path)
t.Fail()
write = true
}
} else {
t.Errorf("missing openapi spec for: %s", path)
write = true
}
if write {
e2 := os.WriteFile(fpath, []byte(pretty), 0644)
if e2 != nil {
t.Errorf("error writing file: %s", e2.Error())
}
}
}
})
}

View File

@ -20,9 +20,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/version"
yamlutil "k8s.io/apimachinery/pkg/util/yaml"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
@ -608,31 +606,6 @@ func (c *K8sTestHelper) NewDiscoveryClient() *discovery.DiscoveryClient {
return client
}
func (c *K8sTestHelper) GetVersionInfo() apimachineryversion.Info {
c.t.Helper()
disco := c.NewDiscoveryClient()
req := disco.RESTClient().Get().
Prefix("version").
SetHeader("Accept", "application/json")
result := req.Do(context.Background())
require.NoError(c.t, result.Error())
raw, err := result.Raw()
require.NoError(c.t, err)
info := apimachineryversion.Info{}
err = json.Unmarshal(raw, &info)
require.NoError(c.t, err)
// Make sure the gitVersion is parsable
v, err := version.Parse(info.GitVersion)
require.NoError(c.t, err)
require.Equal(c.t, info.Major, fmt.Sprintf("%d", v.Major()))
require.Equal(c.t, info.Minor, fmt.Sprintf("%d", v.Minor()))
return info
}
func (c *K8sTestHelper) GetGroupVersionInfoJSON(group string) string {
c.t.Helper()

View File

@ -49,9 +49,6 @@ func TestIntegrationPlaylist(t *testing.T) {
EnableFeatureToggles: []string{},
}))
// Ensure the k8s version is valid
_ = h.GetVersionInfo()
// The accepted verbs will change when dual write is enabled
disco := h.GetGroupVersionInfoJSON("playlist.grafana.app")
// fmt.Printf("%s", disco)