Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions cue-test/common/datasource.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright The Perses Authors
// 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 common

myDsVarSelector: #datasourceSelector & { _kind: "MyDatasource" }

myDsVarSelector: #datasourceSelector & { datasource: "$dsVar" }
4 changes: 2 additions & 2 deletions cue-test/common/format.cue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
package common

myFormat: #format & {
decimalPlaces: 0
shortValues: false
decimalPlaces: 0
shortValues: false
}
37 changes: 37 additions & 0 deletions cue-test/common/proxy/http.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright The Perses Authors
// 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 proxy

myDirectSpec: #baseHTTPDatasourceSpec & {
directUrl: "http://localhost:8080"
}

myProxySpec: #baseHTTPDatasourceSpec & {
proxy: #HTTPProxy & {
kind: "HTTPProxy"
spec: {
url: "https://prometheus.demo.prometheus.io"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get why you are adding default value here.

Specially considering this is supposed to be used for datasource like Pyroscope. The allowedEndpoints list does not match the spec of Pyroscope.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a test file ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh ... right, sorry about that. LGTM then

allowedEndpoints: [
{
endpointPattern: "/api/v1/labels"
method: "POST"
},
{
endpointPattern: "/api/v1/series"
method: "POST"
},
]
}
}
}
5 changes: 3 additions & 2 deletions cue/common/datasource.cue
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
package common

#datasourceSelector: {
_kind: string
datasource?: =~#variableSyntaxRegex | {
kind: string
kind: _kind
name?: string
}
}
}
2 changes: 2 additions & 0 deletions cue/common/proxy/http.cue
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ import (
secret?: string
}
}

#baseHTTPDatasourceSpec: { directUrl: common.#url } | { proxy: #HTTPProxy }
98 changes: 66 additions & 32 deletions scripts/test-cue/test-cue.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import (
"github.com/sirupsen/logrus"
)

// This script goes through the CUE files and validates each of them against its
// corresponding test file, if it exists.
// This script validates CUE schema packages against their corresponding test packages.
// It merges all .cue files within each package directory to properly handle imports and
// package-level definitions.

const (
schemasDir = "cue"
Expand All @@ -33,50 +34,81 @@ const (
// dirsInScope specifies which subdirectories under cue/ to validate
var dirsInScope = []string{"common"}

func findCueFiles(baseDir string, subDir string) ([]string, error) {
var files []string
dirPath := filepath.Join(baseDir, subDir)
// NB: this function assume 1 dirInScope = 1 package. CUE allows multiple packages per dirInScope, but this is not used here.
func findPackages(basePath string, dirInScope string) ([]string, error) {
var packages []string
dirPath := filepath.Join(basePath, dirInScope)

// Include the root directory itself
packages = append(packages, dirInScope)

err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

if !info.IsDir() && filepath.Ext(path) == ".cue" {
// Convert to relative path from baseDir
relPath, err := filepath.Rel(baseDir, path)
if info.IsDir() && path != dirPath {
relPath, err := filepath.Rel(basePath, path)
if err != nil {
return err
}
files = append(files, relPath)
packages = append(packages, relPath)
}

return nil
})

return files, err
return packages, err
}

func fileExists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}
// vetPackage validates CUE files in schemaDir against test files in testDir.
// It collects all .cue files from both directories and runs `cue vet` on them together,
// allowing CUE to merge files in the same package and resolve imports properly.
// The command runs from schemasDir to ensure cue.mod/module.cue is accessible for imports.
func vetPackage(schemaDir, testDir string) error {
logrus.Debugf("Validating package %s against %s", schemaDir, testDir)

// Get list of all .cue files in both directories
schemaFiles, err := filepath.Glob(filepath.Join(schemaDir, "*.cue"))
if err != nil {
return fmt.Errorf("failed to glob schema files: %w", err)
}
testFiles, err := filepath.Glob(filepath.Join(testDir, "*.cue"))
if err != nil {
return fmt.Errorf("failed to glob test files: %w", err)
}

func runCueVet(schemaFile, testFile string) error {
logrus.Debugf("Validating %s against %s", schemaFile, testFile)
// Build command args with paths relative to schemasDir (cue/)
args := []string{"vet"}
for _, f := range schemaFiles {
rel, err := filepath.Rel(schemasDir, f)
if err != nil {
return fmt.Errorf("failed to get relative path for %s: %w", f, err)
}
args = append(args, rel)
}
for _, f := range testFiles {
// testFiles are in ../cue-test relative to schemasDir
rel, err := filepath.Rel(schemasDir, f)
if err != nil {
return fmt.Errorf("failed to get relative path for %s: %w", f, err)
}
args = append(args, rel)
}

cmd := exec.Command("cue", "vet", "-c", schemaFile, testFile)
cmd := exec.Command("cue", args...)
cmd.Dir = schemasDir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to validate %s: %w", schemaFile, err)
return fmt.Errorf("failed to validate %s: %w", schemaDir, err)
}

return nil
}

func validateCueFiles() error {
func validateCueSchemas() error {
logrus.Debugf("Starting CUE files validation")

// Check if cue command is available
Expand All @@ -88,25 +120,27 @@ func validateCueFiles() error {
skippedCount := 0
errCount := 0

for _, subDir := range dirsInScope {
logrus.Debugf("Processing directory: %s", subDir)
files, err := findCueFiles(schemasDir, subDir)
for _, dirInScope := range dirsInScope {
logrus.Debugf("Processing directory: %s", dirInScope)
packageDirs, err := findPackages(schemasDir, dirInScope)
if err != nil {
return fmt.Errorf("failed to find CUE files in %s/%s: %w", schemasDir, subDir, err)
return fmt.Errorf("failed to find directories in %s/%s: %w", schemasDir, dirInScope, err)
}

for _, file := range files {
schemaFile := filepath.Join(schemasDir, file)
testFile := filepath.Join(testDir, file)
if !fileExists(testFile) {
logrus.Debugf("Skipping %s: test file %s not found", schemaFile, testFile)
for _, packageDir := range packageDirs {
schemaDir := filepath.Join(schemasDir, packageDir)
testDir := filepath.Join(testDir, packageDir)

// Check if corresponding test directory exists
if _, err := os.Stat(testDir); os.IsNotExist(err) {
logrus.Debugf("Skipping %s: test directory %s not found", schemaDir, testDir)
skippedCount++
continue
}

logrus.Infof("Validating %s with test file %s", schemaFile, testFile)
if err := runCueVet(schemaFile, testFile); err != nil {
logrus.Errorf("Validation failed for %s: %v", schemaFile, err)
logrus.Infof("Validating package %s with test package %s", schemaDir, testDir)
if err := vetPackage(schemaDir, testDir); err != nil {
logrus.Errorf("Validation failed for %s: %v", schemaDir, err)
errCount++
}

Expand All @@ -122,7 +156,7 @@ func validateCueFiles() error {
}

func main() {
if err := validateCueFiles(); err != nil {
if err := validateCueSchemas(); err != nil {
logrus.Fatal(err)
}
}