Skip to content

Commit 710de7a

Browse files
authored
feat: add image capabilities, test suites, and publish config (#101)
Extends TOML format to support defining capability properties on images, defining publishing targets for images, and adding an initial stub for test suite definitions. Future work will iterate on the latter.
1 parent feeb59a commit 710de7a

File tree

15 files changed

+953
-4
lines changed

15 files changed

+953
-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)