-
Notifications
You must be signed in to change notification settings - Fork 49
/
manifest.go
213 lines (187 loc) · 6.36 KB
/
manifest.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Package manifest is used to define an osbuild manifest as a series of
// pipelines with content. Typically, a Manifest is created using
// manifest.New() and pipelines are defined and added to it using the pipeline
// constructors (e.g., NewBuild()) with the manifest as the first argument. The
// pipelines are added in the order they are called.
//
// The package implements a standard set of osbuild pipelines. A pipeline
// conceptually represents a named filesystem tree, optionally generated
// in a provided build root (represented by another pipeline). All inputs
// to a pipeline must be explicitly specified, either in terms of another
// pipeline, in terms of content addressable inputs or in terms of static
// parameters to the inherited Pipeline structs.
package manifest
import (
"encoding/json"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/ostree"
"github.com/osbuild/images/pkg/rpmmd"
)
type Arch uint64
const (
ARCH_X86_64 Arch = iota
ARCH_AARCH64
ARCH_S390X
ARCH_PPC64LE
)
type Distro uint64
const (
DISTRO_NULL = iota
DISTRO_EL10
DISTRO_EL9
DISTRO_EL8
DISTRO_EL7
DISTRO_FEDORA
)
// An OSBuildManifest is an opaque JSON object, which is a valid input to osbuild
type OSBuildManifest []byte
func (m OSBuildManifest) MarshalJSON() ([]byte, error) {
return json.RawMessage(m).MarshalJSON()
}
func (m *OSBuildManifest) UnmarshalJSON(payload []byte) error {
var raw json.RawMessage
err := (&raw).UnmarshalJSON(payload)
if err != nil {
return err
}
*m = OSBuildManifest(raw)
return nil
}
// Manifest represents a manifest initialised with all the information required
// to generate the pipelines but no content. The content type sources
// (PackageSetChains, ContainerSourceSpecs, OSTreeSourceSpecs) must be
// retrieved through their corresponding Getters and resolved before
// serializing.
type Manifest struct {
// pipelines describe the build process for an image.
pipelines []Pipeline
// Distro defines the distribution of the image that this manifest will
// generate. It is used for determining package names that differ between
// different distributions and version.
Distro Distro
}
func New() Manifest {
return Manifest{
pipelines: make([]Pipeline, 0),
Distro: DISTRO_NULL,
}
}
func (m *Manifest) addPipeline(p Pipeline) {
for _, pipeline := range m.pipelines {
if pipeline.Name() == p.Name() {
panic("duplicate pipeline name in manifest")
}
}
if p.Manifest() != nil {
panic("pipeline already added to a different manifest")
}
m.pipelines = append(m.pipelines, p)
p.setManifest(m)
// check that the pipeline's build pipeline is included in the same manifest
if build := p.BuildPipeline(); build != nil && build.Manifest() != m {
panic("cannot add pipeline to a different manifest than its build pipeline")
}
}
type PackageSelector func([]rpmmd.PackageSet) []rpmmd.PackageSet
func (m Manifest) GetPackageSetChains() map[string][]rpmmd.PackageSet {
chains := make(map[string][]rpmmd.PackageSet)
for _, pipeline := range m.pipelines {
if chain := pipeline.getPackageSetChain(m.Distro); chain != nil {
chains[pipeline.Name()] = chain
}
}
return chains
}
func (m Manifest) GetContainerSourceSpecs() map[string][]container.SourceSpec {
// Containers should only appear in the payload pipeline.
// Let's iterate over all pipelines to avoid assuming pipeline names, but
// return all the specs as a single slice.
containerSpecs := make(map[string][]container.SourceSpec)
for _, pipeline := range m.pipelines {
if containers := pipeline.getContainerSources(); len(containers) > 0 {
containerSpecs[pipeline.Name()] = containers
}
}
return containerSpecs
}
func (m Manifest) GetOSTreeSourceSpecs() map[string][]ostree.SourceSpec {
// OSTree commits should only appear in one pipeline.
// Let's iterate over all pipelines to avoid assuming pipeline names, but
// return all the specs as a single slice if there are multiple.
ostreeSpecs := make(map[string][]ostree.SourceSpec)
for _, pipeline := range m.pipelines {
if commits := pipeline.getOSTreeCommitSources(); len(commits) > 0 {
ostreeSpecs[pipeline.Name()] = commits
}
}
return ostreeSpecs
}
func (m Manifest) Serialize(packageSets map[string][]rpmmd.PackageSpec, containerSpecs map[string][]container.Spec, ostreeCommits map[string][]ostree.CommitSpec, rpmRepos map[string][]rpmmd.RepoConfig) (OSBuildManifest, error) {
pipelines := make([]osbuild.Pipeline, 0)
packages := make([]rpmmd.PackageSpec, 0)
commits := make([]ostree.CommitSpec, 0)
inline := make([]string, 0)
containers := make([]container.Spec, 0)
for _, pipeline := range m.pipelines {
pipeline.serializeStart(packageSets[pipeline.Name()], containerSpecs[pipeline.Name()], ostreeCommits[pipeline.Name()], rpmRepos[pipeline.Name()])
}
for _, pipeline := range m.pipelines {
commits = append(commits, pipeline.getOSTreeCommits()...)
pipelines = append(pipelines, pipeline.serialize())
packages = append(packages, packageSets[pipeline.Name()]...)
inline = append(inline, pipeline.getInline()...)
containers = append(containers, pipeline.getContainerSpecs()...)
}
for _, pipeline := range m.pipelines {
pipeline.serializeEnd()
}
sources, err := osbuild.GenSources(packages, commits, inline, containers)
if err != nil {
return nil, err
}
return json.Marshal(
osbuild.Manifest{
Version: "2",
Pipelines: pipelines,
Sources: sources,
},
)
}
func (m Manifest) GetCheckpoints() []string {
checkpoints := []string{}
for _, p := range m.pipelines {
if p.getCheckpoint() {
checkpoints = append(checkpoints, p.Name())
}
}
return checkpoints
}
func (m Manifest) GetExports() []string {
exports := []string{}
for _, p := range m.pipelines {
if p.getExport() {
exports = append(exports, p.Name())
}
}
return exports
}
// filterRepos returns a list of repositories that specify the given pipeline
// name in their PackageSets list in addition to any global repositories
// (global repositories are ones that do not specify any PackageSets).
func filterRepos(repos []rpmmd.RepoConfig, plName string) []rpmmd.RepoConfig {
filtered := make([]rpmmd.RepoConfig, 0, len(repos))
for _, repo := range repos {
if len(repo.PackageSets) == 0 {
filtered = append(filtered, repo)
continue
}
for _, ps := range repo.PackageSets {
if ps == plName {
filtered = append(filtered, repo)
continue
}
}
}
return filtered
}