Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions docs/release-manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Ultimately, there are two types of release manifests:
* [Solution Release Manifest](#solution-release-manifest)
* [Core Platform Release Manifest](#core-platform-release-manifest)

## Schema versions

Manifests may declare a schema version with a top-level `schema` field. Both `v0` and `v1` are accepted; when the field is omitted, the manifest is treated as `v0`. The `lifecycle` section is optional; when supplied, `availabilityDate` is required and all other lifecycle fields remain optional.

## Solution Release Manifest

> **NOTE:** Elemental is in active development and the Solution manifest API may change over time.
Expand All @@ -20,10 +24,14 @@ Enables consumers to extend a specific `Core Platform` release with additional c
Consumers who wish to create a release manifest for their solution should refer to the below API reference for information.

```yaml
schema: v1
metadata:
name: "SUSE Solution"
version: "4.2.0"
creationDate: "2025-07-10"
lifecycle:
availabilityDate: "2025-07-10"
fullSupportEndDate: "2026-07-10"
maintenanceSupportEndDate: "2027-07-10"
corePlatform:
image: "registry.suse.com/uc/release-manifest:0.0.1"
components:
Expand Down Expand Up @@ -54,10 +62,14 @@ components:
url: "https://charts.jetstack.io"
```

* `schema` - Optional; Schema version of this manifest. Accepted values are `v0` and `v1`; defaults to `v0` when omitted.
* `metadata` - Optional; General information about the solution version that this manifest describes.
* `name` - Required; Name of the solution that this manifest describes.
* `version` - Required; Version of the solution release that this manifest describes.
* `creationDate` - Optional; Defines the release date for the specified version.
* `lifecycle` - Optional; Release lifecycle metadata. When supplied, dates must use ISO `YYYY-MM-DD` format.
* `availabilityDate` - Required (when `lifecycle` is present); Date when this release became available.
* `fullSupportEndDate` - Optional; Date when full support for this release ends.
* `maintenanceSupportEndDate` - Optional; Date when maintenance support for this release ends.
* `corePlatform` - Required; Defines the `Core Platform` release version that this solution wishes to be based upon and extend.
* `image` - Required; Container image pointing to the desired `Core Platform` release manifest.
* `components` - Optional; Components with which to extend the `Core Platform`.
Expand Down Expand Up @@ -102,10 +114,14 @@ Defines the set of components that make up a specific `Core Platform` release ve
```yaml
# The values shown in this example are for illustrative purposes only
# and should not be used directly
schema: v1
metadata:
name: "SUSE Core Platform"
version: "0.0.2"
creationDate: "2025-07-14"
lifecycle:
availabilityDate: "2025-07-14"
fullSupportEndDate: "2026-07-14"
maintenanceSupportEndDate: "2027-07-14"
components:
operatingSystem:
image:
Expand All @@ -126,7 +142,7 @@ components:
url: "https://metallb.github.io/metallb"
```

The manifest's structure is similar to that of the [Solution Release Manifest](#solution-release-manifest-api), with the key difference being the inclusion of components unique to the Core Platform (e.g. `operatingSystem` and `kubernetes`).
The manifest's structure is similar to that of the [Solution Release Manifest](#solution-release-manifest-api), with the key difference being the inclusion of components unique to the Core Platform (e.g. `operatingSystem` and `kubernetes`). The `schema`, `metadata`, and `lifecycle` sections follow the same rules as in the `Solution Release Manifest`.

This reference focuses only on the unique to the Core Platform component APIs. Any components not mentioned here share the same description as those in the `Solution Release Manifest`.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
schema: v1
metadata:
name: "suse-solution"
version: "4.2.0"
creationDate: "2025-12-08"
lifecycle:
availabilityDate: "2025-12-08"
fullSupportEndDate: "2026-12-08"
maintenanceSupportEndDate: "2027-12-08"
corePlatform:
# Registry path to the release manifest OCI image of the Core Platform that this SUSE Solution extends
image: "registry.suse.com/beta/uc/release-manifest:0.6_rke2_1.35"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# This configuration represents a fictional SUSE Solution and is meant for example purposes only.
schema: v1
metadata:
name: "suse-solution"
version: "4.2.0"
creationDate: "2025-12-08"
lifecycle:
availabilityDate: "2025-12-08"
fullSupportEndDate: "2026-12-08"
maintenanceSupportEndDate: "2027-12-08"
corePlatform:
# Release manifest OCI image of the Core Platform that this SUSE Solution extends
image: "registry.suse.com/beta/uc/release-manifest:0.6_rke2_1.35"
Expand Down
57 changes: 38 additions & 19 deletions internal/cli/action/release_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,9 @@ const (
var markdown bool

type basicInfo struct {
Name string
Version string
CreationDate string
Source string
Name string
Version string
Source string
}

func ReleaseInfo(_ context.Context, cmd *cli.Command) error {
Expand Down Expand Up @@ -224,42 +223,62 @@ func newTable(markdown bool, out io.Writer) *tablewriter.Table {
// most basic information that shall be printed for all the optional flags
func printBasicData(cm *core.ReleaseManifest, sm *solution.ReleaseManifest, arg string, out io.Writer) error {
var data [][]string
var cmBasic, smBasic *basicInfo
table := newTable(markdown, out)

cmBasic = &basicInfo{
Name: cm.Metadata.Name,
Version: cm.Metadata.Version,
CreationDate: cm.Metadata.CreationDate,
Source: arg,
cmBasic := &basicInfo{
Name: cm.Metadata.Name,
Version: cm.Metadata.Version,
Source: arg,
}
cmLifecycle := lifecycleRowValues(cm.Lifecycle)

if sm != nil {
// we are dealing with a solution manifest
smBasic = &basicInfo{
Name: sm.Metadata.Name,
Version: sm.Metadata.Version,
CreationDate: sm.Metadata.CreationDate,
Source: arg,
smBasic := &basicInfo{
Name: sm.Metadata.Name,
Version: sm.Metadata.Version,
Source: arg,
}
smLifecycle := lifecycleRowValues(sm.Lifecycle)
cmBasic.Source = sm.CorePlatform.Image

table.Header([]string{"Attribute", "Core Platform (Base)", "Solution Manifest (Extension)"})
data = append(data, []string{"Name", cmBasic.Name, smBasic.Name})
data = append(data, []string{versionHdr, cmBasic.Version, sm.Metadata.Version})
data = append(data, []string{"Release Date", cmBasic.CreationDate, sm.Metadata.CreationDate})
data = append(data, []string{versionHdr, cmBasic.Version, smBasic.Version})
data = append(data, []string{"Availability Date", cmLifecycle.availability, smLifecycle.availability})
data = append(data, []string{"Full Support End", cmLifecycle.fullSupportEnd, smLifecycle.fullSupportEnd})
data = append(data, []string{"Maintenance Support End", cmLifecycle.maintenanceSupportEnd, smLifecycle.maintenanceSupportEnd})
data = append(data, []string{sourceHdr, cmBasic.Source, smBasic.Source})
} else {
// we are dealing with a core manifest
table.Header([]string{"Attribute", "Core Platform (Base)"})
data = append(data, []string{"Name", cmBasic.Name})
data = append(data, []string{versionHdr, cmBasic.Version})
data = append(data, []string{"Release Data", cmBasic.CreationDate})
data = append(data, []string{"Availability Date", cmLifecycle.availability})
data = append(data, []string{"Full Support End", cmLifecycle.fullSupportEnd})
data = append(data, []string{"Maintenance Support End", cmLifecycle.maintenanceSupportEnd})
data = append(data, []string{sourceHdr, cmBasic.Source})

}
return printAndClearData(table, data, out)
}

type lifecycleRow struct {
availability string
fullSupportEnd string
maintenanceSupportEnd string
}

func lifecycleRowValues(l *api.Lifecycle) lifecycleRow {
if l == nil {
return lifecycleRow{}
}
return lifecycleRow{
availability: l.AvailabilityDate,
fullSupportEnd: l.FullSupportEndDate,
maintenanceSupportEnd: l.MaintenanceSupportEndDate,
}
}

func printInfraData(cm *core.ReleaseManifest, out io.Writer) error {
var data [][]string
table := newTable(markdown, out)
Expand Down
8 changes: 6 additions & 2 deletions internal/cli/action/release_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ var _ = Describe("Release info tests", Label("release-info"), func() {
var cliCmd *cli.Command
var buffer *bytes.Buffer
var ctx context.Context
var manifest = `metadata:
var manifest = `schema: v1
metadata:
name: suse-core-test
version: 0.6-rc.20260317
creationDate: '2026-03-17'
lifecycle:
availabilityDate: '2026-03-17'
fullSupportEndDate: '2027-03-17'
maintenanceSupportEndDate: '2028-03-17'
components:
operatingSystem:
image:
Expand Down
8 changes: 7 additions & 1 deletion pkg/manifest/api/core/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
type ReleaseManifest struct {
Schema api.SchemaVersion `yaml:"schema,omitempty"`
Metadata *api.Metadata `yaml:"metadata,omitempty"`
Lifecycle *api.Lifecycle `yaml:"lifecycle,omitempty"`
Components Components `yaml:"components" validate:"required"`
}

Expand All @@ -56,7 +57,8 @@ type Image struct {
}

func Parse(data []byte) (*ReleaseManifest, error) {
if _, err := api.LoadSchemaVersion(data); err != nil {
schema, err := api.LoadSchemaVersion(data)
if err != nil {
return nil, fmt.Errorf("parsing 'core' release manifest: %w", err)
}

Expand All @@ -77,5 +79,9 @@ func Parse(data []byte) (*ReleaseManifest, error) {
return nil, fmt.Errorf("validating 'core' release manifest: %w", err)
}

if err := api.ValidateMetadata(schema, rm.Metadata); err != nil {
return nil, fmt.Errorf("validating 'core' release manifest: %w", err)
}

return rm, nil
}
Loading