|
15 | 15 | package recipe |
16 | 16 |
|
17 | 17 | import ( |
| 18 | + "bytes" |
18 | 19 | "context" |
| 20 | + stderrors "errors" |
19 | 21 | "maps" |
20 | 22 | "slices" |
21 | 23 | "strings" |
22 | 24 | "testing" |
23 | 25 |
|
| 26 | + "github.com/NVIDIA/aicr/pkg/errors" |
24 | 27 | "gopkg.in/yaml.v3" |
25 | 28 | ) |
26 | 29 |
|
@@ -1200,3 +1203,85 @@ func TestLoadRegistry_RejectsReservedDeployerKey(t *testing.T) { |
1200 | 1203 | }) |
1201 | 1204 | } |
1202 | 1205 | } |
| 1206 | + |
| 1207 | +// TestLoadRegistry_RejectsKustomizeManifestFiles verifies the registry |
| 1208 | +// loader fails closed when a Kustomize-typed component declares |
| 1209 | +// registry-level manifestFiles defaults. validateComponentRef rejects |
| 1210 | +// Kustomize refs carrying manifestFiles, so a registry entry combining |
| 1211 | +// both would be clone-filled into every referencing recipe and fail |
| 1212 | +// resolution in every consumer at once — surface the config mistake at |
| 1213 | +// load time instead (same fail-closed contract as the reserved |
| 1214 | +// deployer-key guard above). |
| 1215 | +func TestLoadRegistry_RejectsKustomizeManifestFiles(t *testing.T) { |
| 1216 | + registryYAML := "apiVersion: aicr.run/v1alpha2\n" + |
| 1217 | + "kind: ComponentRegistry\n" + |
| 1218 | + "components:\n" + |
| 1219 | + " - name: my-kustomize-app\n" + |
| 1220 | + " displayName: My Kustomize App\n" + |
| 1221 | + " kustomize:\n" + |
| 1222 | + " defaultSource: https://github.com/example/my-app\n" + |
| 1223 | + " defaultPath: deploy/production\n" + |
| 1224 | + " defaultTag: v1.0.0\n" + |
| 1225 | + " manifestFiles:\n" + |
| 1226 | + " - components/my-kustomize-app/manifests/extra.yaml\n" |
| 1227 | + dp := newInMemoryProvider("kustomize-manifestfiles", map[string][]byte{ |
| 1228 | + "registry.yaml": []byte(registryYAML), |
| 1229 | + }) |
| 1230 | + _, err := GetComponentRegistryFor(dp) |
| 1231 | + if err == nil { |
| 1232 | + t.Fatal("expected registry load to fail on Kustomize component with manifestFiles, got nil error") |
| 1233 | + } |
| 1234 | + if !strings.Contains(err.Error(), `"my-kustomize-app"`) { |
| 1235 | + t.Errorf("error %q does not name the offending component", err.Error()) |
| 1236 | + } |
| 1237 | + if !strings.Contains(err.Error(), "manifestFiles") { |
| 1238 | + t.Errorf("error %q does not mention manifestFiles", err.Error()) |
| 1239 | + } |
| 1240 | + // Fail-closed contract: the guard must surface a 4xx invalid-request |
| 1241 | + // (a registry misconfiguration is a bad input, not an internal fault), |
| 1242 | + // mirroring the coherence-check precedent in |
| 1243 | + // componentref_coherence_test.go. Asserting only the message would stay |
| 1244 | + // green if the guard's code silently regressed to ErrCodeInternal. |
| 1245 | + if !stderrors.Is(err, errors.New(errors.ErrCodeInvalidRequest, "")) { |
| 1246 | + t.Errorf("want ErrCodeInvalidRequest, got %v", err) |
| 1247 | + } |
| 1248 | +} |
| 1249 | + |
| 1250 | +func TestComponentRegistry_ManifestFilesResolve(t *testing.T) { |
| 1251 | + provider := NewEmbeddedDataProvider(GetEmbeddedFS(), "") |
| 1252 | + registry, err := GetComponentRegistryFor(provider) |
| 1253 | + if err != nil { |
| 1254 | + t.Fatalf("failed to load component registry: %v", err) |
| 1255 | + } |
| 1256 | + for _, comp := range registry.Components { |
| 1257 | + for _, mf := range comp.ManifestFiles { |
| 1258 | + data, err := provider.ReadFile(context.Background(), mf) |
| 1259 | + if err != nil { |
| 1260 | + t.Errorf("component %q manifestFiles entry %q is not readable from embedded data: %v", |
| 1261 | + comp.Name, mf, err) |
| 1262 | + continue |
| 1263 | + } |
| 1264 | + if len(bytes.TrimSpace(data)) == 0 { |
| 1265 | + t.Errorf("component %q manifestFiles entry %q is empty", comp.Name, mf) |
| 1266 | + } |
| 1267 | + } |
| 1268 | + } |
| 1269 | + |
| 1270 | + // Regression sentinel pinned to kueue: a generic "some component has |
| 1271 | + // manifestFiles" check stays green if kueue's quota entries are |
| 1272 | + // dropped while another component still declares a list. Assert the |
| 1273 | + // kueue component and its exact quota CR paths. |
| 1274 | + kueue := registry.Get("kueue") |
| 1275 | + if kueue == nil { |
| 1276 | + t.Fatal("registry has no kueue component") |
| 1277 | + } |
| 1278 | + wantManifests := []string{ |
| 1279 | + "components/kueue/manifests/resource-flavor.yaml", |
| 1280 | + "components/kueue/manifests/cluster-queue.yaml", |
| 1281 | + "components/kueue/manifests/local-queue.yaml", |
| 1282 | + } |
| 1283 | + if !slices.Equal(kueue.ManifestFiles, wantManifests) { |
| 1284 | + t.Errorf("kueue manifestFiles = %v, want %v (dependency-ordered quota CRs)", |
| 1285 | + kueue.ManifestFiles, wantManifests) |
| 1286 | + } |
| 1287 | +} |
0 commit comments