Skip to content

Commit 90baee1

Browse files
[ENHANCEMENT] cue/common: provide new standard definitions for datasource plugins
Signed-off-by: AntoineThebaud <antoine.thebaud@yahoo.fr>
1 parent 653507d commit 90baee1

6 files changed

Lines changed: 128 additions & 36 deletions

File tree

cue-test/common/datasource.cue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright The Perses Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package common
15+
16+
myDsVarSelector: #datasourceSelector & { _kind: "MyDatasource" }
17+
18+
myDsVarSelector: #datasourceSelector & { datasource: "$dsVar" }

cue-test/common/format.cue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@
1414
package common
1515

1616
myFormat: #format & {
17-
decimalPlaces: 0
18-
shortValues: false
17+
decimalPlaces: 0
18+
shortValues: false
1919
}

cue-test/common/proxy/http.cue

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright The Perses Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package proxy
15+
16+
myDirectSpec: #baseHTTPDatasourceSpec & {
17+
directUrl: "http://localhost:8080"
18+
}
19+
20+
myProxySpec: #baseHTTPDatasourceSpec & {
21+
proxy: #HTTPProxy & {
22+
kind: "HTTPProxy"
23+
spec: {
24+
url: "https://prometheus.demo.prometheus.io"
25+
allowedEndpoints: [
26+
{
27+
endpointPattern: "/api/v1/labels"
28+
method: "POST"
29+
},
30+
{
31+
endpointPattern: "/api/v1/series"
32+
method: "POST"
33+
},
34+
]
35+
}
36+
}
37+
}

cue/common/datasource.cue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
package common
1515

1616
#datasourceSelector: {
17+
_kind: string
1718
datasource?: =~#variableSyntaxRegex | {
18-
kind: string
19+
kind: _kind
1920
name?: string
2021
}
21-
}
22+
}

cue/common/proxy/http.cue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,5 @@ import (
3838
secret?: string
3939
}
4040
}
41+
42+
#baseHTTPDatasourceSpec: { directUrl: common.#url } | { proxy: #HTTPProxy }

scripts/test-cue/test-cue.go

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ import (
2222
"github.com/sirupsen/logrus"
2323
)
2424

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

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

36-
func findCueFiles(baseDir string, subDir string) ([]string, error) {
37-
var files []string
38-
dirPath := filepath.Join(baseDir, subDir)
37+
// NB: this function assume 1 dirInScope = 1 package. CUE allows multiple packages per dirInScope, but this is not used here.
38+
func findPackages(basePath string, dirInScope string) ([]string, error) {
39+
var packages []string
40+
dirPath := filepath.Join(basePath, dirInScope)
41+
42+
// Include the root directory itself
43+
packages = append(packages, dirInScope)
3944

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

45-
if !info.IsDir() && filepath.Ext(path) == ".cue" {
46-
// Convert to relative path from baseDir
47-
relPath, err := filepath.Rel(baseDir, path)
50+
if info.IsDir() && path != dirPath {
51+
relPath, err := filepath.Rel(basePath, path)
4852
if err != nil {
4953
return err
5054
}
51-
files = append(files, relPath)
55+
packages = append(packages, relPath)
5256
}
5357

5458
return nil
5559
})
5660

57-
return files, err
61+
return packages, err
5862
}
5963

60-
func fileExists(path string) bool {
61-
_, err := os.Stat(path)
62-
return !os.IsNotExist(err)
63-
}
64+
// vetPackage validates CUE files in schemaDir against test files in testDir.
65+
// It collects all .cue files from both directories and runs `cue vet` on them together,
66+
// allowing CUE to merge files in the same package and resolve imports properly.
67+
// The command runs from schemasDir to ensure cue.mod/module.cue is accessible for imports.
68+
func vetPackage(schemaDir, testDir string) error {
69+
logrus.Debugf("Validating package %s against %s", schemaDir, testDir)
70+
71+
// Get list of all .cue files in both directories
72+
schemaFiles, err := filepath.Glob(filepath.Join(schemaDir, "*.cue"))
73+
if err != nil {
74+
return fmt.Errorf("failed to glob schema files: %w", err)
75+
}
76+
testFiles, err := filepath.Glob(filepath.Join(testDir, "*.cue"))
77+
if err != nil {
78+
return fmt.Errorf("failed to glob test files: %w", err)
79+
}
6480

65-
func runCueVet(schemaFile, testFile string) error {
66-
logrus.Debugf("Validating %s against %s", schemaFile, testFile)
81+
// Build command args with paths relative to schemasDir (cue/)
82+
args := []string{"vet"}
83+
for _, f := range schemaFiles {
84+
rel, err := filepath.Rel(schemasDir, f)
85+
if err != nil {
86+
return fmt.Errorf("failed to get relative path for %s: %w", f, err)
87+
}
88+
args = append(args, rel)
89+
}
90+
for _, f := range testFiles {
91+
// testFiles are in ../cue-test relative to schemasDir
92+
rel, err := filepath.Rel(schemasDir, f)
93+
if err != nil {
94+
return fmt.Errorf("failed to get relative path for %s: %w", f, err)
95+
}
96+
args = append(args, rel)
97+
}
6798

68-
cmd := exec.Command("cue", "vet", "-c", schemaFile, testFile)
99+
cmd := exec.Command("cue", args...)
100+
cmd.Dir = schemasDir
69101
cmd.Stdout = os.Stdout
70102
cmd.Stderr = os.Stderr
71103

72104
if err := cmd.Run(); err != nil {
73-
return fmt.Errorf("failed to validate %s: %w", schemaFile, err)
105+
return fmt.Errorf("failed to validate %s: %w", schemaDir, err)
74106
}
75107

76108
return nil
77109
}
78110

79-
func validateCueFiles() error {
111+
func validateCueSchemas() error {
80112
logrus.Debugf("Starting CUE files validation")
81113

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

91-
for _, subDir := range dirsInScope {
92-
logrus.Debugf("Processing directory: %s", subDir)
93-
files, err := findCueFiles(schemasDir, subDir)
123+
for _, dirInScope := range dirsInScope {
124+
logrus.Debugf("Processing directory: %s", dirInScope)
125+
packageDirs, err := findPackages(schemasDir, dirInScope)
94126
if err != nil {
95-
return fmt.Errorf("failed to find CUE files in %s/%s: %w", schemasDir, subDir, err)
127+
return fmt.Errorf("failed to find directories in %s/%s: %w", schemasDir, dirInScope, err)
96128
}
97129

98-
for _, file := range files {
99-
schemaFile := filepath.Join(schemasDir, file)
100-
testFile := filepath.Join(testDir, file)
101-
if !fileExists(testFile) {
102-
logrus.Debugf("Skipping %s: test file %s not found", schemaFile, testFile)
130+
for _, packageDir := range packageDirs {
131+
schemaDir := filepath.Join(schemasDir, packageDir)
132+
testDir := filepath.Join(testDir, packageDir)
133+
134+
// Check if corresponding test directory exists
135+
if _, err := os.Stat(testDir); os.IsNotExist(err) {
136+
logrus.Debugf("Skipping %s: test directory %s not found", schemaDir, testDir)
103137
skippedCount++
104138
continue
105139
}
106140

107-
logrus.Infof("Validating %s with test file %s", schemaFile, testFile)
108-
if err := runCueVet(schemaFile, testFile); err != nil {
109-
logrus.Errorf("Validation failed for %s: %v", schemaFile, err)
141+
logrus.Infof("Validating package %s with test package %s", schemaDir, testDir)
142+
if err := vetPackage(schemaDir, testDir); err != nil {
143+
logrus.Errorf("Validation failed for %s: %v", schemaDir, err)
110144
errCount++
111145
}
112146

@@ -122,7 +156,7 @@ func validateCueFiles() error {
122156
}
123157

124158
func main() {
125-
if err := validateCueFiles(); err != nil {
159+
if err := validateCueSchemas(); err != nil {
126160
logrus.Fatal(err)
127161
}
128162
}

0 commit comments

Comments
 (0)