Skip to content

Commit 357feb1

Browse files
reubenoCopilot
andcommitted
feat: add image capabilities, test suites, and publish config
Add structured metadata to image configuration: - ImageCapabilities: tri-state boolean fields (machine-bootable, container, systemd, runtime-package-management) with *bool semantics for correct merge and underspecification detection - ImageTestsConfig: link images to named test suites defined in the new top-level [test-suites] section, with cross-reference validation - ImagePublishConfig: multi-channel publish support (channels list), unlike packages which target a single channel New top-level [test-suites] config section: - TestSuiteConfig with name and description fields - Loaded via mergeTestSuites in the config loader (duplicates rejected) - Image-to-test-suite references validated in ProjectConfig.Validate() Expose metadata in 'azldev image list': - Structured JSON output preserves original projectconfig types - Table output shows comma-separated summaries for capabilities, tests, and publish channels Documentation: - images.md: capabilities, tests, and publish field references with examples - test-suites.md: new reference page for test suite configuration - config-file.md: added test-suites, default-package-config, and package-groups to the top-level sections table Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent feeb59a commit 357feb1

File tree

12 files changed

+647
-4
lines changed

12 files changed

+647
-4
lines changed

docs/user/reference/config/config-file.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ All config files share the same schema — there is no distinction between a "ro
1414
| `components` | map of objects | Component (package) definitions | [Components](components.md) |
1515
| `component-groups` | map of objects | Named groups of components with shared defaults | [Component Groups](component-groups.md) |
1616
| `images` | map of objects | Image definitions (VMs, containers) | [Images](images.md) |
17+
| `test-suites` | map of objects | Named test suite definitions referenced by images | [Test Suites](test-suites.md) |
1718
| `tools` | object | Configuration for external tools used by azldev | [Tools](tools.md) |
19+
| `default-package-config` | object | Project-wide default applied to all binary packages | [Package Groups — Resolution Order](package-groups.md#resolution-order) |
20+
| `package-groups` | map of objects | Named groups of binary packages with shared config | [Package Groups](package-groups.md) |
1821

1922
## Includes
2023

docs/user/reference/config/images.md

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ The `[images]` section defines system images (VMs, containers, etc.) that azldev
88
|-------|----------|------|----------|-------------|
99
| Description | `description` | string | No | Human-readable description of the image |
1010
| Definition | `definition` | [ImageDefinition](#image-definition) | No | Specifies the image definition format, file path, and optional profile |
11+
| Capabilities | `capabilities` | [ImageCapabilities](#image-capabilities) | No | Describes features and properties of this image |
12+
| Tests | `tests` | [ImageTests](#image-tests) | No | Test configuration for this image |
13+
| Publish | `publish` | [ImagePublish](#image-publish) | No | Publishing settings for this image |
1114

1215
## Image Definition
1316

@@ -19,24 +22,59 @@ The `definition` field tells azldev where to find the image definition file and
1922
| Path | `path` | string | No | Path to the image definition file, relative to the config file |
2023
| Profile | `profile` | string | No | Build profile to use when building the image (format-specific) |
2124

25+
## Image Capabilities
26+
27+
The `capabilities` subtable describes what the image supports. All fields are optional booleans using tri-state semantics: `true` (explicitly enabled), `false` (explicitly disabled), or omitted (unspecified / inherit from defaults).
28+
29+
| Field | TOML Key | Type | Default | Description |
30+
|-------|----------|------|---------|-------------|
31+
| Machine Bootable | `machine-bootable` | bool | unset | Whether the image can be booted on a machine (bare metal or VM) |
32+
| Container | `container` | bool | unset | Whether the image can be run on an OCI container host |
33+
| Systemd | `systemd` | bool | unset | Whether the image runs systemd as its init system |
34+
| Runtime Package Management | `runtime-package-management` | bool | unset | Whether the image supports installing/removing packages at runtime (e.g., via dnf/tdnf) |
35+
36+
## Image Tests
37+
38+
The `tests` subtable links an image to one or more test suites defined in the top-level [`[test-suites]`](test-suites.md) section.
39+
40+
| Field | TOML Key | Type | Required | Description |
41+
|-------|----------|------|----------|-------------|
42+
| Test Suites | `test-suites` | array of inline tables | No | List of test suite references. Each entry must have a `name` field matching a key in `[test-suites]`. |
43+
44+
## Image Publish
45+
46+
The `publish` subtable configures where an image is published. Unlike packages (which target a single channel), images may be published to multiple channels simultaneously.
47+
48+
| Field | TOML Key | Type | Required | Description |
49+
|-------|----------|------|----------|-------------|
50+
| Channels | `channels` | string array | No | List of publish channels for this image |
51+
2252
> **Note:** Each image name must be unique across all config files. Defining the same image name in two files produces an error.
2353
2454
## Examples
2555

26-
### VM image using Kiwi
56+
### VM image with capabilities
2757

2858
```toml
2959
[images.vm-base]
3060
description = "VM Base Image"
3161
definition = { type = "kiwi", path = "vm-base/vm-base.kiwi" }
62+
63+
[images.vm-base.capabilities]
64+
machine-bootable = true
65+
systemd = true
66+
runtime-package-management = true
3267
```
3368

34-
### Container image
69+
### Container image with capabilities
3570

3671
```toml
3772
[images.container-base]
3873
description = "Container Base Image"
3974
definition = { type = "kiwi", path = "container-base/container-base.kiwi" }
75+
76+
[images.container-base.capabilities]
77+
container = true
4078
```
4179

4280
### Image with a build profile
@@ -47,7 +85,37 @@ description = "Azure-optimized VM image"
4785
definition = { type = "kiwi", path = "vm-azure/vm-azure.kiwi", profile = "azure" }
4886
```
4987

88+
### Image with test suite references
89+
90+
```toml
91+
[images.vm-base]
92+
description = "VM Base Image"
93+
definition = { type = "kiwi", path = "vm-base/vm-base.kiwi" }
94+
95+
[images.vm-base.capabilities]
96+
machine-bootable = true
97+
systemd = true
98+
99+
[images.vm-base.tests]
100+
test-suites = [
101+
{ name = "smoke" },
102+
{ name = "integration" },
103+
]
104+
```
105+
106+
### Image with publish channels
107+
108+
```toml
109+
[images.vm-base]
110+
description = "VM Base Image"
111+
definition = { type = "kiwi", path = "vm-base/vm-base.kiwi" }
112+
113+
[images.vm-base.publish]
114+
channels = ["registry-prod", "registry-staging"]
115+
```
116+
50117
## Related Resources
51118

52119
- [Config File Structure](config-file.md) — top-level config file layout
120+
- [Test Suites](test-suites.md) — test suite definitions
53121
- [Tools](tools.md) — Image Customizer tool configuration
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Test Suites
2+
3+
The `[test-suites]` section defines named test suites that can be referenced by images. Each test suite is defined under `[test-suites.<name>]`.
4+
5+
## Test Suite Config
6+
7+
| Field | TOML Key | Type | Required | Description |
8+
|-------|----------|------|----------|-------------|
9+
| Description | `description` | string | No | Human-readable description of the test suite |
10+
11+
Test suites are referenced by images through the [`[images.<name>.tests]`](images.md#image-tests) subtable. Each image can reference one or more test suites by name.
12+
13+
> **Note:** Each test suite name must be unique across all config files. Defining the same test suite name in two files produces an error.
14+
15+
## Examples
16+
17+
### Basic test suite definitions
18+
19+
```toml
20+
[test-suites.smoke]
21+
description = "Smoke tests for basic image validation"
22+
23+
[test-suites.integration]
24+
description = "Integration tests for live VM validation"
25+
```
26+
27+
### Referencing test suites from an image
28+
29+
```toml
30+
[test-suites.smoke]
31+
description = "Smoke tests"
32+
33+
[images.vm-base]
34+
description = "VM Base Image"
35+
36+
[images.vm-base.tests]
37+
test-suites = [{ name = "smoke" }]
38+
```
39+
40+
## Related Resources
41+
42+
- [Images](images.md) — image configuration including test references
43+
- [Config File Structure](config-file.md) — top-level config file layout

internal/app/azldev/cmds/image/list.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111

1212
"github.com/microsoft/azure-linux-dev-tools/internal/app/azldev"
13+
"github.com/microsoft/azure-linux-dev-tools/internal/projectconfig"
1314
"github.com/samber/lo"
1415
"github.com/spf13/cobra"
1516
)
@@ -28,6 +29,26 @@ type ImageListResult struct {
2829
// Description of the image.
2930
Description string `json:"description"`
3031

32+
// Capabilities describes the features and properties of this image.
33+
Capabilities projectconfig.ImageCapabilities `json:"capabilities" table:"-"`
34+
35+
// CapabilitiesSummary is a comma-separated summary of enabled capabilities for table
36+
// display.
37+
CapabilitiesSummary string `json:"-" table:"Capabilities"`
38+
39+
// Tests holds the test configuration for this image, matching the original config
40+
// structure.
41+
Tests projectconfig.ImageTestsConfig `json:"tests" table:"-"`
42+
43+
// TestsSummary is a comma-separated summary of test suite names for table display.
44+
TestsSummary string `json:"-" table:"Tests"`
45+
46+
// Publish holds the publish settings for this image.
47+
Publish projectconfig.ImagePublishConfig `json:"publish" table:"-"`
48+
49+
// PublishSummary is a comma-separated summary of publish channels for table display.
50+
PublishSummary string `json:"-" table:"Publish"`
51+
3152
// Definition contains the image definition details (hidden from table output).
3253
Definition ImageDefinitionResult `json:"definition" table:"-"`
3354
}
@@ -108,9 +129,16 @@ func ListImages(env *azldev.Env, options *ListImageOptions) ([]ImageListResult,
108129
}
109130

110131
imageConfig := cfg.Images[name]
132+
111133
results = append(results, ImageListResult{
112-
Name: name,
113-
Description: imageConfig.Description,
134+
Name: name,
135+
Description: imageConfig.Description,
136+
Capabilities: imageConfig.Capabilities,
137+
CapabilitiesSummary: strings.Join(imageConfig.Capabilities.EnabledNames(), ", "),
138+
Tests: imageConfig.Tests,
139+
TestsSummary: strings.Join(imageConfig.TestNames(), ", "),
140+
Publish: imageConfig.Publish,
141+
PublishSummary: strings.Join(imageConfig.Publish.Channels, ", "),
114142
Definition: ImageDefinitionResult{
115143
Type: string(imageConfig.Definition.DefinitionType),
116144
Path: imageConfig.Definition.Path,

internal/app/azldev/cmds/image/list_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/microsoft/azure-linux-dev-tools/internal/app/azldev/cmds/image"
1010
"github.com/microsoft/azure-linux-dev-tools/internal/app/azldev/core/testutils"
1111
"github.com/microsoft/azure-linux-dev-tools/internal/projectconfig"
12+
"github.com/samber/lo"
1213
"github.com/stretchr/testify/assert"
1314
"github.com/stretchr/testify/require"
1415
)
@@ -78,6 +79,91 @@ func TestListImages_AllImages(t *testing.T) {
7879
assert.Equal(t, "Image B description", results[1].Description)
7980
}
8081

82+
func TestListImages_WithCapabilitiesAndTests(t *testing.T) {
83+
testEnv := testutils.NewTestEnv(t)
84+
testEnv.Config.Images = map[string]projectconfig.ImageConfig{
85+
"vm-base": {
86+
Name: "vm-base",
87+
Description: "VM Base Image",
88+
Capabilities: projectconfig.ImageCapabilities{
89+
MachineBootable: lo.ToPtr(true),
90+
Systemd: lo.ToPtr(true),
91+
},
92+
Tests: projectconfig.ImageTestsConfig{
93+
TestSuites: []projectconfig.TestSuiteRef{
94+
{Name: "smoke"},
95+
{Name: "integration"},
96+
},
97+
},
98+
Publish: projectconfig.ImagePublishConfig{
99+
Channels: []string{"registry-prod", "registry-staging"},
100+
},
101+
},
102+
"container-base": {
103+
Name: "container-base",
104+
Description: "Container Base Image",
105+
Capabilities: projectconfig.ImageCapabilities{
106+
Container: lo.ToPtr(true),
107+
},
108+
Tests: projectconfig.ImageTestsConfig{
109+
TestSuites: []projectconfig.TestSuiteRef{
110+
{Name: "smoke"},
111+
},
112+
},
113+
Publish: projectconfig.ImagePublishConfig{
114+
Channels: []string{"registry-prod"},
115+
},
116+
},
117+
"minimal": {
118+
Name: "minimal",
119+
Description: "Minimal image with no capabilities or tests",
120+
},
121+
}
122+
123+
options := &image.ListImageOptions{}
124+
125+
results, err := image.ListImages(testEnv.Env, options)
126+
require.NoError(t, err)
127+
require.Len(t, results, 3)
128+
129+
// Results sorted alphabetically.
130+
assert.Equal(t, "container-base", results[0].Name)
131+
assert.Equal(t, lo.ToPtr(true), results[0].Capabilities.Container)
132+
assert.Nil(t, results[0].Capabilities.MachineBootable)
133+
assert.Equal(t, "container", results[0].CapabilitiesSummary)
134+
assert.Equal(t, projectconfig.ImageTestsConfig{
135+
TestSuites: []projectconfig.TestSuiteRef{{Name: "smoke"}},
136+
}, results[0].Tests)
137+
assert.Equal(t, "smoke", results[0].TestsSummary)
138+
assert.Equal(t, projectconfig.ImagePublishConfig{
139+
Channels: []string{"registry-prod"},
140+
}, results[0].Publish)
141+
assert.Equal(t, "registry-prod", results[0].PublishSummary)
142+
143+
assert.Equal(t, "minimal", results[1].Name)
144+
assert.Nil(t, results[1].Capabilities.MachineBootable)
145+
assert.Nil(t, results[1].Capabilities.Container)
146+
assert.Empty(t, results[1].CapabilitiesSummary)
147+
assert.Empty(t, results[1].Tests.TestSuites)
148+
assert.Empty(t, results[1].TestsSummary)
149+
assert.Empty(t, results[1].Publish.Channels)
150+
assert.Empty(t, results[1].PublishSummary)
151+
152+
assert.Equal(t, "vm-base", results[2].Name)
153+
assert.Equal(t, lo.ToPtr(true), results[2].Capabilities.MachineBootable)
154+
assert.Equal(t, lo.ToPtr(true), results[2].Capabilities.Systemd)
155+
assert.Nil(t, results[2].Capabilities.Container)
156+
assert.Equal(t, "machine-bootable, systemd", results[2].CapabilitiesSummary)
157+
assert.Equal(t, projectconfig.ImageTestsConfig{
158+
TestSuites: []projectconfig.TestSuiteRef{{Name: "smoke"}, {Name: "integration"}},
159+
}, results[2].Tests)
160+
assert.Equal(t, "smoke, integration", results[2].TestsSummary)
161+
assert.Equal(t, projectconfig.ImagePublishConfig{
162+
Channels: []string{"registry-prod", "registry-staging"},
163+
}, results[2].Publish)
164+
assert.Equal(t, "registry-prod, registry-staging", results[2].PublishSummary)
165+
}
166+
81167
func TestListImages_ExactMatch(t *testing.T) {
82168
testEnv := testutils.NewTestEnv(t)
83169
testEnv.Config.Images = map[string]projectconfig.ImageConfig{

internal/projectconfig/configfile.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ type ConfigFile struct {
5454
// to be applied to sets of binary packages.
5555
PackageGroups map[string]PackageGroupConfig `toml:"package-groups,omitempty" validate:"dive" jsonschema:"title=Package groups,description=Definitions of package groups for shared binary package configuration"`
5656

57+
// Definitions of test suites.
58+
TestSuites map[string]TestSuiteConfig `toml:"test-suites,omitempty" validate:"dive" jsonschema:"title=Test Suites,description=Definitions of test suites for this project"`
59+
5760
// Internal fields used to track the origin of the config file; `dir` is the directory
5861
// that the config file's relative paths are based from.
5962
sourcePath string `toml:"-"`

0 commit comments

Comments
 (0)