Skip to content

Commit 2f468d2

Browse files
authored
Merge pull request #3043 from dheerajodha/EC-1534
Extract .spec from cluster record format for snapshot input
2 parents fd24bdd + 8114042 commit 2f468d2

File tree

2 files changed

+109
-4
lines changed

2 files changed

+109
-4
lines changed

internal/applicationsnapshot/input.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,15 +183,29 @@ func DetermineInputSpec(ctx context.Context, input Input) (*app.SnapshotSpec, *E
183183
}
184184

185185
func readSnapshotSource(input []byte) (app.SnapshotSpec, error) {
186-
var file app.SnapshotSpec
187-
err := yaml.Unmarshal(input, &file)
188-
if err != nil {
186+
// Define a temporary struct to capture the wrapped spec so we
187+
// can read snapshot data correctly from a cluster record
188+
var wrapper struct {
189+
Spec *app.SnapshotSpec `yaml:"spec"`
190+
}
191+
192+
// Attempt to unmarshal into the wrapper to check for cluster record format
193+
if err := yaml.Unmarshal(input, &wrapper); err == nil && wrapper.Spec != nil {
194+
// If successful and spec exists, return it directly
195+
log.Debugf("Read application snapshot from cluster record format")
196+
return *wrapper.Spec, nil
197+
}
198+
199+
// If we didn't find a snapshot under the .spec top level key then
200+
// assume we're looking at the bare snapshot data
201+
var spec app.SnapshotSpec
202+
if err := yaml.Unmarshal(input, &spec); err != nil {
189203
log.Debugf("Problem parsing application snapshot from file %s", input)
190204
return app.SnapshotSpec{}, fmt.Errorf("unable to parse Snapshot specification from %s: %w", input, err)
191205
}
192206

193207
log.Debugf("Read application snapshot from file %s", input)
194-
return file, nil
208+
return spec, nil
195209
}
196210

197211
// For an image index, remove the original component and replace it with an expanded component with all its image manifests

internal/applicationsnapshot/input_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,97 @@ func TestReadSnapshotFile(t *testing.T) {
233233
expected := fmt.Errorf("unable to parse Snapshot specification from %s: %w", spec, wrapped)
234234
assert.Error(t, err, expected)
235235
})
236+
237+
t.Run("Snapshot with .spec wrapper (cluster record format)", func(t *testing.T) {
238+
snapshotSpec := app.SnapshotSpec{
239+
Components: []app.SnapshotComponent{
240+
{
241+
Name: "Named",
242+
ContainerImage: "registry.io/repository/image:tag",
243+
},
244+
},
245+
}
246+
fs := afero.NewMemMapFs()
247+
// Simulate a cluster record format with .spec wrapper
248+
clusterRecord := `{
249+
"apiVersion": "appstudio.redhat.com/v1alpha1",
250+
"kind": "Snapshot",
251+
"metadata": {
252+
"name": "vsa-demo-app-x9xln",
253+
"namespace": "user-ns2"
254+
},
255+
"spec": {
256+
"components": [
257+
{
258+
"name": "Named",
259+
"containerImage": "registry.io/repository/image:tag"
260+
}
261+
]
262+
}
263+
}`
264+
265+
err := afero.WriteFile(fs, "/cluster-record.json", []byte(clusterRecord), 0644)
266+
if err != nil {
267+
t.Fatalf("Setup failure: could not write file: %v", err)
268+
}
269+
270+
content, err := afero.ReadFile(fs, "/cluster-record.json")
271+
assert.NoError(t, err)
272+
got, err := readSnapshotSource(content)
273+
assert.NoError(t, err)
274+
assert.Equal(t, snapshotSpec, got)
275+
})
276+
277+
t.Run("Snapshot with .spec wrapper in YAML format", func(t *testing.T) {
278+
snapshotSpec := app.SnapshotSpec{
279+
Components: []app.SnapshotComponent{
280+
{
281+
Name: "Named",
282+
ContainerImage: "registry.io/repository/image:tag",
283+
},
284+
},
285+
}
286+
// Simulate a cluster record format with .spec wrapper in YAML
287+
clusterRecord := `apiVersion: appstudio.redhat.com/v1alpha1
288+
kind: Snapshot
289+
metadata:
290+
name: vsa-demo-app-x9xln
291+
namespace: user-ns2
292+
spec:
293+
components:
294+
- name: Named
295+
containerImage: registry.io/repository/image:tag`
296+
297+
content := []byte(clusterRecord)
298+
got, err := readSnapshotSource(content)
299+
assert.NoError(t, err)
300+
assert.Equal(t, snapshotSpec, got)
301+
})
302+
303+
t.Run("Snapshot without .spec wrapper (backward compatibility)", func(t *testing.T) {
304+
snapshotSpec := app.SnapshotSpec{
305+
Components: []app.SnapshotComponent{
306+
{
307+
Name: "Named",
308+
ContainerImage: "registry.io/repository/image:tag",
309+
},
310+
},
311+
}
312+
// Direct SnapshotSpec without wrapper (existing behavior)
313+
directSpec := `{
314+
"components": [
315+
{
316+
"name": "Named",
317+
"containerImage": "registry.io/repository/image:tag"
318+
}
319+
]
320+
}`
321+
322+
content := []byte(directSpec)
323+
got, err := readSnapshotSource(content)
324+
assert.NoError(t, err)
325+
assert.Equal(t, snapshotSpec, got)
326+
})
236327
}
237328

238329
func TestExpandImageIndex(t *testing.T) {

0 commit comments

Comments
 (0)