debian-forge-composer/internal/manifest/manifest.go
Tom Gundersen 1cb2f0276d manifest: make packageSpecs optional
Allow manifests to be instantiated without providing packageSpecs.
This allows manifests without packageSpecs to be introspected, but
not serialized.

The only reason we used to require packegaSpecs to be passed at
instantiation time was to compute the kernelVer based on the
kernelName and the NEVRAs in the package set. Now move this to be
done at serialize time.

This means that in order to serialize a manifest, you need to pass
the packageSpecs at initialization time, but if you don't need to
serialize, only to introspect it, then you don't need to pass it
at all.

In a future patch packageSpecs will be passed at serialization time
to not have to make that commitment up front.

The new logic is now that before a pipeline can be serialized, all
the pipelines in the manifest must be "prepared" for serialization
by calling serialize_start() on each of them, which does whatever
would in the past have been done at initialization time which
required the pacakgeSpecs. Once serialization has been finished
serialize_end() must be called on each of the pipelines to undo
any of the preparation, making sure we can serialize repeatedly,
possibly while changing the fields of our pipeline in-between,
and that serialization appears from the outside to not have any
side-effects.

In a future PR we may want to rework this logic to instead use a
serialization context.
2022-07-04 23:04:29 +01:00

99 lines
2.4 KiB
Go

package manifest
import (
"encoding/json"
"github.com/osbuild/osbuild-composer/internal/distro"
"github.com/osbuild/osbuild-composer/internal/osbuild2"
"github.com/osbuild/osbuild-composer/internal/ostree"
"github.com/osbuild/osbuild-composer/internal/rpmmd"
)
type osTreeCommit struct {
checksum string
url string
}
// An OSBuildManifest is an opaque JSON object, which is a valid input to osbuild
// TODO: use this instead of distro.Manifest below
type OSBuildManifest []byte
type Manifest struct {
pipelines []Pipeline
packageSpecs []rpmmd.PackageSpec
osTreeCommits []osTreeCommit
inlineData []string
}
func New() Manifest {
return Manifest{
pipelines: make([]Pipeline, 0),
packageSpecs: make([]rpmmd.PackageSpec, 0),
osTreeCommits: make([]osTreeCommit, 0),
inlineData: make([]string, 0),
}
}
func (m *Manifest) AddPipeline(p Pipeline) {
for _, pipeline := range m.pipelines {
if pipeline.Name() == p.Name() {
panic("duplicate pipeline name in manifest")
}
}
m.pipelines = append(m.pipelines, p)
m.addPackages(p.getPackageSpecs())
m.addOSTreeCommits(p.getOSTreeCommits())
m.addInline(p.getInline())
}
func (m *Manifest) addPackages(packages []rpmmd.PackageSpec) {
m.packageSpecs = append(m.packageSpecs, packages...)
}
func (m *Manifest) addOSTreeCommits(commits []osTreeCommit) {
m.osTreeCommits = append(m.osTreeCommits, commits...)
}
func (m *Manifest) addInline(data []string) {
m.inlineData = append(m.inlineData, data...)
}
func (m Manifest) GetPackageSetChains() map[string][]rpmmd.PackageSet {
chains := make(map[string][]rpmmd.PackageSet)
for _, pipeline := range m.pipelines {
chains[pipeline.Name()] = pipeline.getPackageSetChain()
}
return chains
}
func (m Manifest) Serialize() (distro.Manifest, error) {
var commits []ostree.CommitSource
for _, commit := range m.osTreeCommits {
commits = []ostree.CommitSource{
{
Checksum: commit.checksum, URL: commit.url,
},
}
}
pipelines := make([]osbuild2.Pipeline, 0)
for _, pipeline := range m.pipelines {
pipeline.serializeStart()
}
for _, pipeline := range m.pipelines {
pipelines = append(pipelines, pipeline.serialize())
}
for _, pipeline := range m.pipelines {
pipeline.serializeEnd()
}
return json.Marshal(
osbuild2.Manifest{
Version: "2",
Pipelines: pipelines,
Sources: osbuild2.GenSources(m.packageSpecs, commits, m.inlineData),
},
)
}