Skip to content

Commit f6cbcb2

Browse files
committed
fix: harden schema migration guidance #368
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 9214524 commit f6cbcb2

5 files changed

Lines changed: 69 additions & 10 deletions

File tree

cmd/waza/cmd_migrate.go

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package main
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"io"
67
"os"
78
"path/filepath"
89

910
"github.com/microsoft/waza/internal/models"
1011
"github.com/spf13/cobra"
12+
"gopkg.in/yaml.v3"
1113
)
1214

1315
func newMigrateCommand() *cobra.Command {
@@ -28,20 +30,52 @@ migration steps here.`,
2830
}
2931

3032
func runMigrate(out io.Writer, path string) error {
31-
if _, err := os.Stat(path); err != nil {
33+
data, err := os.ReadFile(path)
34+
if err != nil {
3235
return fmt.Errorf("reading %s: %w", path, err)
3336
}
3437

35-
artifact := "artifact"
38+
artifact, version, err := readArtifactSchemaVersion(path, data)
39+
if err != nil {
40+
return err
41+
}
42+
43+
version, err = models.ValidateSchemaVersion(artifact, path, version)
44+
if err != nil {
45+
return err
46+
}
47+
48+
if version != models.CurrentSchemaVersion {
49+
_, err = fmt.Fprintf(out, "%s uses schemaVersion %s, which is compatible with waza schemaVersion %s; no migration needed.\n", artifact, version, models.CurrentSchemaVersion)
50+
return err
51+
}
52+
53+
_, err = fmt.Fprintf(out, "%s is already compatible with schemaVersion %s; no migration needed.\n", artifact, models.CurrentSchemaVersion)
54+
return err
55+
}
56+
57+
func readArtifactSchemaVersion(path string, data []byte) (artifact string, version string, err error) {
3658
switch filepath.Base(path) {
3759
case "eval.yaml", "eval.yml":
3860
artifact = "eval.yaml"
61+
var header struct {
62+
SchemaVersion string `yaml:"schemaVersion"`
63+
}
64+
if err := yaml.Unmarshal(data, &header); err != nil {
65+
return "", "", fmt.Errorf("parsing %s: %w", path, err)
66+
}
67+
return artifact, header.SchemaVersion, nil
3968
default:
4069
if filepath.Ext(path) == ".json" {
4170
artifact = "results.json"
71+
var header struct {
72+
SchemaVersion string `json:"schemaVersion"`
73+
}
74+
if err := json.Unmarshal(data, &header); err != nil {
75+
return "", "", fmt.Errorf("parsing %s: %w", path, err)
76+
}
77+
return artifact, header.SchemaVersion, nil
4278
}
4379
}
44-
45-
_, err := fmt.Fprintf(out, "%s is already compatible with schemaVersion %s; no migration needed.\n", artifact, models.CurrentSchemaVersion)
46-
return err
80+
return "", "", fmt.Errorf("unsupported schema artifact %s: expected eval.yaml, eval.yml, or a JSON results artifact", path)
4781
}

cmd/waza/cmd_migrate_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,31 @@ func TestMigrateCommandNoOpForCurrentMajor(t *testing.T) {
2121
require.Contains(t, out.String(), "already compatible with schemaVersion 1.0")
2222
}
2323

24+
func TestMigrateCommandRejectsIncompatibleMajor(t *testing.T) {
25+
dir := t.TempDir()
26+
path := filepath.Join(dir, "results.json")
27+
require.NoError(t, os.WriteFile(path, []byte(`{"schemaVersion":"2.0"}`), 0o644))
28+
29+
var out bytes.Buffer
30+
err := runMigrate(&out, path)
31+
32+
require.Error(t, err)
33+
require.Contains(t, err.Error(), `waza migrate <file>`)
34+
require.Empty(t, out.String())
35+
}
36+
37+
func TestMigrateCommandDefaultsMissingSchemaVersion(t *testing.T) {
38+
dir := t.TempDir()
39+
path := filepath.Join(dir, "results.json")
40+
require.NoError(t, os.WriteFile(path, []byte(`{"eval_id":"run-1"}`), 0o644))
41+
42+
var out bytes.Buffer
43+
err := runMigrate(&out, path)
44+
45+
require.NoError(t, err)
46+
require.Contains(t, out.String(), "already compatible with schemaVersion 1.0")
47+
}
48+
2449
func TestMigrateCommandMissingFile(t *testing.T) {
2550
var out bytes.Buffer
2651
err := runMigrate(&out, filepath.Join(t.TempDir(), "missing.json"))

internal/models/schema_version.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func defaultSchemaVersion(version string) string {
2525
return version
2626
}
2727

28-
func validateSchemaVersion(artifact, source, version string) (string, error) {
28+
func ValidateSchemaVersion(artifact, source, version string) (string, error) {
2929
version = defaultSchemaVersion(version)
3030

3131
major, _, err := parseSchemaVersion(version)
@@ -37,7 +37,7 @@ func validateSchemaVersion(artifact, source, version string) (string, error) {
3737
return "", err
3838
}
3939
if major != currentMajor {
40-
return "", fmt.Errorf("%s %s uses schemaVersion %q, but this waza supports schemaVersion %s; run \"waza migrate %s\" to migrate the file", artifact, source, version, CurrentSchemaVersion, source)
40+
return "", fmt.Errorf("%s %s uses schemaVersion %q, but this waza supports schemaVersion %s; run \"waza migrate <file>\" to migrate the artifact file", artifact, source, version, CurrentSchemaVersion)
4141
}
4242
return version, nil
4343
}
@@ -121,7 +121,7 @@ func ParseEvaluationOutcome(data []byte, source string) (*EvaluationOutcome, err
121121
if err := json.Unmarshal(data, &header); err != nil {
122122
return nil, err
123123
}
124-
version, err := validateSchemaVersion("results.json", source, header.SchemaVersion)
124+
version, err := ValidateSchemaVersion("results.json", source, header.SchemaVersion)
125125
if err != nil {
126126
return nil, err
127127
}

internal/models/spec.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ func LoadEvalSpec(path string) (*EvalSpec, error) {
309309
if err := yaml.Unmarshal(data, &header); err != nil {
310310
return nil, fmt.Errorf("parsing eval spec YAML (%s): %w", path, err)
311311
}
312-
version, err := validateSchemaVersion("eval.yaml", path, header.SchemaVersion)
312+
version, err := ValidateSchemaVersion("eval.yaml", path, header.SchemaVersion)
313313
if err != nil {
314314
return nil, err
315315
}

internal/storage/azure_blob.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ func (abs *AzureBlobStore) Download(ctx context.Context, runID string) (*models.
239239
return nil, fmt.Errorf("azure blob download: reading blob: %w", err)
240240
}
241241

242-
outcome, err := models.ParseEvaluationOutcome(data, runID)
242+
outcome, err := models.ParseEvaluationOutcome(data, blobPath)
243243
if err != nil {
244244
return nil, fmt.Errorf("azure blob download: unmarshaling outcome: %w", err)
245245
}

0 commit comments

Comments
 (0)