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.
This commit is contained in:
Tom Gundersen 2022-06-29 11:59:46 +01:00
parent 8f9b6eef8f
commit 1cb2f0276d
4 changed files with 56 additions and 7 deletions

View file

@ -27,6 +27,7 @@ type AnacondaPipeline struct {
repos []rpmmd.RepoConfig
packageSpecs []rpmmd.PackageSpec
kernelName string
kernelVer string
arch string
product string
@ -46,12 +47,11 @@ func NewAnacondaPipeline(buildPipeline *BuildPipeline,
arch,
product,
version string) *AnacondaPipeline {
kernelVer := rpmmd.GetVerStrFromPackageSpecListPanic(packages, kernelName)
p := &AnacondaPipeline{
BasePipeline: NewBasePipeline("anaconda-tree", buildPipeline, nil),
repos: repos,
packageSpecs: packages,
kernelVer: kernelVer,
kernelName: kernelName,
arch: arch,
product: product,
version: version,
@ -74,6 +74,20 @@ func (p *AnacondaPipeline) getPackageSpecs() []rpmmd.PackageSpec {
return p.packageSpecs
}
func (p *AnacondaPipeline) serializeStart() {
if p.kernelVer != "" {
panic("double call to serializeStart()")
}
p.kernelVer = rpmmd.GetVerStrFromPackageSpecListPanic(p.packageSpecs, p.kernelName)
}
func (p *AnacondaPipeline) serializeEnd() {
if p.kernelVer == "" {
panic("serializeEnd() call when serialization not in progress")
}
p.kernelVer = ""
}
func (p *AnacondaPipeline) serialize() osbuild2.Pipeline {
pipeline := p.BasePipeline.serialize()

View file

@ -79,9 +79,15 @@ func (m Manifest) Serialize() (distro.Manifest, error) {
}
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{

View file

@ -92,6 +92,7 @@ type OSPipeline struct {
partitionTable *disk.PartitionTable
bootLoader BootLoader
grubLegacy string
kernelName string
kernelVer string
}
@ -115,10 +116,6 @@ func NewOSPipeline(buildPipeline *BuildPipeline,
if osTree {
name = "ostree-tree"
}
var kernelVer string
if kernelName != "" {
kernelVer = rpmmd.GetVerStrFromPackageSpecListPanic(packages, kernelName)
}
p := &OSPipeline{
BasePipeline: NewBasePipeline(name, buildPipeline, nil),
osTree: osTree,
@ -129,7 +126,7 @@ func NewOSPipeline(buildPipeline *BuildPipeline,
partitionTable: partitionTable,
bootLoader: bootLoader,
grubLegacy: grubLegacy,
kernelVer: kernelVer,
kernelName: kernelName,
Language: "C.UTF-8",
Hostname: "localhost.localdomain",
Timezone: "UTC",
@ -174,6 +171,26 @@ func (p *OSPipeline) getPackageSpecs() []rpmmd.PackageSpec {
return p.packageSpecs
}
func (p *OSPipeline) serializeStart() {
if p.kernelName == "" {
return
}
if p.kernelVer != "" {
panic("double call to serializeStart()")
}
p.kernelVer = rpmmd.GetVerStrFromPackageSpecListPanic(p.packageSpecs, p.kernelName)
}
func (p *OSPipeline) serializeEnd() {
if p.kernelName == "" {
return
}
if p.kernelVer == "" {
panic("serializeEnd() call when serialization not in progress")
}
p.kernelVer = ""
}
func (p *OSPipeline) serialize() osbuild2.Pipeline {
pipeline := p.BasePipeline.serialize()

View file

@ -15,6 +15,8 @@ type Pipeline interface {
Name() string
getBuildPackages() []string
getPackageSetChain() []rpmmd.PackageSet
serializeStart()
serializeEnd()
serialize() osbuild2.Pipeline
getPackageSpecs() []rpmmd.PackageSpec
getOSTreeCommits() []osTreeCommit
@ -79,6 +81,16 @@ func NewBasePipeline(name string, build *BuildPipeline, runner *string) BasePipe
return p
}
// serializeStart must be called exactly once before each call
// to serialize().
func (p BasePipeline) serializeStart() {
}
// serializeEnd must be called exactly once after each call to
// serialize().
func (p BasePipeline) serializeEnd() {
}
// Serialize turns a given pipeline into an osbuild2.Pipeline object. This object is
// meant to be treated as opaque and not to be modified further outside of the pipeline
// package.