-
Notifications
You must be signed in to change notification settings - Fork 23
/
common.go
316 lines (283 loc) · 11.5 KB
/
common.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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
//
// SPDX-License-Identifier: Apache-2.0
package main
import (
"fmt"
"strings"
"github.com/open-component-model/ocm/examples/lib/helper"
"github.com/open-component-model/ocm/pkg/blobaccess"
"github.com/open-component-model/ocm/pkg/common"
"github.com/open-component-model/ocm/pkg/contexts/ocm"
"github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociartifact"
"github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1"
"github.com/open-component-model/ocm/pkg/contexts/ocm/elements"
"github.com/open-component-model/ocm/pkg/contexts/ocm/elements/artifactblob/dockermultiblob"
"github.com/open-component-model/ocm/pkg/contexts/ocm/elements/artifactblob/textblob"
"github.com/open-component-model/ocm/pkg/contexts/ocm/resourcetypes"
"github.com/open-component-model/ocm/pkg/contexts/ocm/utils"
"github.com/open-component-model/ocm/pkg/errors"
"github.com/open-component-model/ocm/pkg/finalizer"
"github.com/open-component-model/ocm/pkg/mime"
"github.com/open-component-model/ocm/pkg/semverutils"
)
// setupVersion configures a component version.
// This can be called on any omponent version, regardless of
// its origin.
func setupVersion(cv ocm.ComponentVersionAccess) error {
provider := &compdesc.Provider{
Name: "acme.org",
}
fmt.Printf(" setting provider...\n")
err := cv.SetProvider(provider)
if err != nil {
return errors.Wrapf(err, "cannot set provider")
}
////////////////////////////////////////////////////////////////////////////
// Ok, a component version not describing any resources
// is pretty useless.
// So, lets add some resources, now.
fmt.Printf(" setting external resource 'image'...\n")
// first, we add some resource already located in
// some external registry.
// A resources has some metadata, like an identity
// and a type.
// The identity is just a set of string properties,
// with at least containing the name property.
// additional identity properties can be added via
// options.
meta, err := elements.ResourceMeta("image", resourcetypes.OCI_IMAGE)
if err != nil {
// without metadata options, there will be never be an error,
// bit to be complete, we just handle the error case, here.
return errors.Wrapf(err, "invalid resource meta")
}
// And most important it requires content.
// Content can be already present in some external
// repository. As long, as there is an access type
// for this kind of repository, we can just refer to it.
// Here, we just use an image provided by the
// OCM ecosystem.
// Supported access types can be found under
// .../pkg/contexts/ocm/accessmethods.
acc := ociartifact.New("ghcr.io/open-component-model/ocm/ocm.software/toi/installers/helminstaller/helminstaller:0.4.0")
// Once we have both, the metadata and the content specification,
// we can now add the resource.
// The SetResource methods will replace an existing resource with the same
// identity, or add the resource, if no such resource exists in the component
// version.
err = cv.SetResource(meta, acc)
if err != nil {
return errors.Wrapf(err, "cannot add access to ocmcli-image)")
}
////////////////////////////////////////////////////////////////////////////
// Now, we will add a second resource, some unspecific yaml data.
// Therefore, we use the generic YAML resource type.
// In practice, you should always use a resource type describing
// the real meaning of the content, for example something like
// `kubernetesManifest`, This enables tools working with specific content
// to understand the resource set of a component version.
fmt.Printf(" setting blob resource 'descriptor'...\n")
meta, err = elements.ResourceMeta("descriptor", resourcetypes.OCM_YAML)
if err != nil {
return errors.Wrapf(err, "invalid resource meta")
}
basic := true
yamldata := `
type: mySpecialDocument
data: some very important data required to understand this component
`
if basic {
// Besides referring to external resources, another possibility
// to add content is to directly provide the content blob. The
// used abstraction here is blobaccess.BlobAccess.
// Any blob content provided by an implementation of this
// interface can be added as resource.
// There are various access implementations for blobs
// taken from the local host, for example, from the filesystem,
// or from other repositories (for example by mapping
// an access type specification into a blob access).
// The most simple form is to directly provide a byte sequence,
// for example some YAML data.
// A blob always must provide a mime type, describing the
// technical format of the blob's byte sequence.
blob := blobaccess.ForString(mime.MIME_YAML, yamldata)
// when storing the blob, it is possible to provide some
// optional additional information:
// - a name of the resource described by the blob, which could
// be used to do a later upload into an external repository
// (for example the image repository of an OCI image stored
// as local blob)
// - an additional access type, which provides an alternative
// global technology specific access to the same content.
// we don't use it, here.
err = cv.SetResourceBlob(meta, blob, "", nil)
if err != nil {
return errors.Wrapf(err, "cannot add yaml document")
}
} else {
// The above blob example describes the basic operations,
// which can be used to compose any kind of resource
// from any kind of source.
// For selected use cases there are convenience helpers,
// which can be used to compose a resource access object.
// This is basically the same interface returned by GetResource
// functions on the component version from the last example.
// Such objects can directly be used to add/modify a resource in a
// component version.
// The above case could be written as follows, also:
res := textblob.ResourceAccess(cv.GetContext(), meta, yamldata,
textblob.WithimeType(mime.MIME_YAML))
err = cv.SetResourceAccess(res)
if err != nil {
return errors.Wrapf(err, "cannot add yaml document")
}
}
// There are even more complex blob sources, for example
// for helm charts stored in the filesystem, or even for images
// generated by docker builds.
// Here, we just compose a multi-platform image built with buildx
// from these sources (components/ocmcli) featuring two flavors.
// (you have to execute `make image.multi` in components/ocmcli
// before executing this example.
fmt.Printf(" setting blob resource 'ocmcli'...\n")
meta, err = elements.ResourceMeta("ocmcli", resourcetypes.OCI_IMAGE)
if err != nil {
return errors.Wrapf(err, "invalid resource meta")
}
res := dockermultiblob.ResourceAccess(cv.GetContext(), meta,
dockermultiblob.WithPrinter(common.StdoutPrinter),
dockermultiblob.WithHint("ocm.software/ocmci"),
dockermultiblob.WithVersion(current_version),
dockermultiblob.WithVariants(
fmt.Sprintf("ocmcli-image:%s-linux-amd64", current_version),
fmt.Sprintf("ocmcli-image:%s-linux-arm64", current_version),
),
)
err = cv.SetResourceAccess(res)
if err != nil {
return errors.Wrapf(err, "cannot add ocmcli")
}
return err
}
// addVersion configures and adds a new version to the
// given repository.
// This can be called for any repository object, regardless of its
// underlying storage backend.
func addVersion(repo ocm.Repository, name, version string) error {
// basically, any other OCM repository implementation could
// be used here.
// now we compose a new component version, first we create
// a new version backed by this repository.
cv, err := repo.NewComponentVersion(name, version)
if err != nil {
return errors.Wrapf(err, "cannot create new version")
}
defer cv.Close()
err = setupVersion(cv)
if err != nil {
return errors.Wrapf(err, "cannot setup new version")
}
// finally, wee add the new version to the repository.
fmt.Printf("adding component version\n")
err = repo.AddComponentVersion(cv)
if err != nil {
return errors.Wrapf(err, "cannot save version")
}
return nil
}
func describeVersion(cv ocm.ComponentVersionAccess) error {
// many elements of the API keep trak of their context
ctx := cv.GetContext()
// Have a look at the component descriptor
cd := cv.GetDescriptor()
fmt.Printf("resources of the latest version of %s:\n", cv.GetName())
fmt.Printf(" version: %s\n", cv.GetVersion())
fmt.Printf(" provider: %s\n", cd.Provider.Name)
// and list all the included resources.
for i, r := range cv.GetResources() {
fmt.Printf(" %2d: name: %s\n", i+1, r.Meta().GetName())
fmt.Printf(" extra identity: %s\n", r.Meta().GetExtraIdentity())
fmt.Printf(" resource type: %s\n", r.Meta().GetType())
acc, err := r.Access()
if err != nil {
fmt.Printf(" access: error: %s\n", err)
} else {
fmt.Printf(" access: %s\n", acc.Describe(ctx))
}
displayDigest(r.Meta().Digest)
}
if len(cd.Signatures) > 0 {
fmt.Printf("signatures:\n")
for i, s := range cd.Signatures {
fmt.Printf(" %2d: name: %s\n", i+1, s.Name)
displayDigest(&s.Digest)
fmt.Printf(" signature:\n")
fmt.Printf(" algorithm: %s\n", s.Signature.Algorithm)
fmt.Printf(" mediaType: %s\n", s.Signature.MediaType)
fmt.Printf(" value: %s\n", s.Signature.Value)
}
}
return nil
}
func displayDigest(d *metav1.DigestSpec) {
if d != nil {
fmt.Printf(" digest:\n")
fmt.Printf(" algorithm: %s\n", d.HashAlgorithm)
fmt.Printf(" normalization: %s\n", d.NormalisationAlgorithm)
fmt.Printf(" value: %s\n", d.Value)
}
}
func listVersions(repo ocm.Repository, list ...string) error {
// now we just list the found components and their latest version.
// because we will loop over versions, we want to
// close the objects acquired in the loop at the end of
// the loop, but on error returns, also.
// This can be achieved using a Finalizer object.
var finalize finalizer.Finalizer
defer finalize.Finalize()
for _, name := range list {
// one instance per loop step, which can separately finalized.
// It is still bound to the top level finalizer and
// will be finalized on return statements.
nested := finalize.Nested()
// Do you remember the code from example 1?
// This is basically the same, used to examine the
// created version
c, err := repo.LookupComponent(name)
if err != nil {
return errors.Wrapf(err, "cannot get component %s", name)
}
nested.Close(c, "component %s", name)
// Now we look for the versions of the component
// available in this repository.
versions, err := c.ListVersions()
if err != nil {
return errors.Wrapf(err, "cannot query version names for %s", name)
}
// OCM version names must follow the semver rules.
err = semverutils.SortVersions(versions)
if err != nil {
return errors.Wrapf(err, "cannot sort versions for %s", name)
}
fmt.Printf("versions for component %s: %s\n", name, strings.Join(versions, ", "))
cv, err := repo.LookupComponentVersion(name, versions[len(versions)-1])
if err != nil {
return errors.Wrapf(err, "cannot get latest version for %s", name)
}
nested.Close(cv, "component version", common.VersionedElementKey(cv).String())
nested.Finalize()
}
return nil
}
func ReadConfiguration(ctx ocm.Context, cfg *helper.Config) error {
if cfg.OCMConfig != "" {
fmt.Printf("*** applying config from %s\n", cfg.OCMConfig)
_, err := utils.Configure(ctx, cfg.OCMConfig)
if err != nil {
return errors.Wrapf(err, "error in ocm config %s", cfg.OCMConfig)
}
}
return nil
}