Skip to content

Commit 2e54f9f

Browse files
feat: add context to support cancel to tool ops (#1380)
<!-- markdownlint-disable MD041 --> #### What this PR does / why we need it This PR changes the internal tool operations for signing and transfer to use a `context.Context`. This context can be used to cancel the operations. The check is always done before a new component version is handled. This means, if cancelled, the actual component version handled after a cancel will always be finished before the operation is cancelled. The consumer facing operations are added to provide versions accepting a context. #### Which issue(s) this PR fixes Fixes #1346 --------- Co-authored-by: Jakob Möller <[email protected]>
1 parent 43632aa commit 2e54f9f

File tree

7 files changed

+169
-18
lines changed

7 files changed

+169
-18
lines changed

api/config/configutils/configure.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,17 @@ import (
99
utils "ocm.software/ocm/api/ocm/ocmutils"
1010
)
1111

12+
// Configure configures the default context applying
13+
// a configuration file.
14+
// It also applies implicit settings from the OCM context.
1215
func Configure(path string, fss ...vfs.FileSystem) error {
1316
_, err := utils.Configure(config.DefaultContext(), path, fss...)
1417
return err
1518
}
1619

20+
// ConfigureContext configures the given context applying
21+
// a configuration file.
22+
// It also applies implicit settings from the OCM context.
1723
func ConfigureContext(ctxp config.ContextProvider, path string, fss ...vfs.FileSystem) error {
1824
_, err := utils.Configure(ctxp, path, fss...)
1925
return err

api/ocm/tools/signing/handle.go

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package signing
22

33
import (
4+
"context"
45
"crypto"
56
"encoding/hex"
67
"fmt"
@@ -107,14 +108,22 @@ func Apply(printer common.Printer, state *WalkingState, cv ocm.ComponentVersionA
107108
opts = opts.Dup()
108109
opts.Printer = printer
109110
}
111+
return ApplyWithContext(context.Background(), state, cv, opts, closecv...)
112+
}
113+
114+
func ApplyWithContext(ctx context.Context, state *WalkingState, cv ocm.ComponentVersionAccess, opts *Options, closecv ...bool) (*metav1.DigestSpec, error) {
115+
if opts.Printer == nil {
116+
opts = opts.Dup()
117+
opts.Printer = common.GetPrinter(ctx)
118+
}
110119
err := opts.Complete(cv.GetContext())
111120
if err != nil {
112121
return nil, err
113122
}
114123
if state == nil {
115124
state = DefaultWalkingState(cv.GetContext())
116125
}
117-
dc, err := apply(*state, cv, opts, general.Optional(closecv...))
126+
dc, err := apply(ctx, *state, cv, opts, general.Optional(closecv...))
118127
if err != nil {
119128
return nil, err
120129
}
@@ -140,7 +149,7 @@ func RequireReProcessing(vi *VersionInfo, ctx *DigestContext, opts *Options) boo
140149
return opts.DoSign() && !vi.digestingContexts[ctx.CtxKey].Signed
141150
}
142151

143-
func apply(state WalkingState, cv ocm.ComponentVersionAccess, opts *Options, closecv bool) (dc *DigestContext, efferr error) {
152+
func apply(ctx context.Context, state WalkingState, cv ocm.ComponentVersionAccess, opts *Options, closecv bool) (dc *DigestContext, efferr error) {
144153
var closer errors.ErrorFunction
145154
if closecv {
146155
closer = func() error {
@@ -163,10 +172,15 @@ func apply(state WalkingState, cv ocm.ComponentVersionAccess, opts *Options, clo
163172
return dc, nil
164173
}
165174
}
166-
return _apply(state, nv, cv, vi, opts)
175+
return _apply(ctx, state, nv, cv, vi, opts)
167176
}
168177

169-
func _apply(state WalkingState, nv common.NameVersion, cv ocm.ComponentVersionAccess, vi *VersionInfo, opts *Options) (*DigestContext, error) { //nolint: maintidx // yes
178+
func _apply(cctx context.Context, state WalkingState, nv common.NameVersion, cv ocm.ComponentVersionAccess, vi *VersionInfo, opts *Options) (*DigestContext, error) { //nolint: maintidx // yes
179+
if err := common.IsContextCanceled(cctx); err != nil {
180+
opts.Printer.Printf("cancelled by caller\n")
181+
return nil, err
182+
}
183+
170184
prefix := ""
171185
var ctx *DigestContext
172186
if vi == nil {
@@ -213,7 +227,7 @@ func _apply(state WalkingState, nv common.NameVersion, cv ocm.ComponentVersionAc
213227
// digests used for the existing signatures.
214228
substate := state
215229
substate.Context = nil
216-
nctx, err := _apply(substate, nv, cv, vi, opts)
230+
nctx, err := _apply(cctx, substate, nv, cv, vi, opts)
217231
if err != nil {
218232
return nil, err
219233
}
@@ -253,10 +267,10 @@ func _apply(state WalkingState, nv common.NameVersion, cv ocm.ComponentVersionAc
253267
var spec *metav1.DigestSpec
254268
legacy := signing.IsLegacyHashAlgorithm(ctx.RootContextInfo.DigestType.HashAlgorithm) && !opts.DoSign()
255269
if ctx.Digest == nil {
256-
if err := calculateReferenceDigests(state, opts, legacy); err != nil {
270+
if err := calculateReferenceDigests(cctx, state, opts, legacy); err != nil {
257271
return nil, err
258272
}
259-
if err := calculateResourceDigests(state, cv, cd, opts, legacy, ctx.GetPreset(ctx.Key)); err != nil {
273+
if err := calculateResourceDigests(cctx, state, cv, cd, opts, legacy, ctx.GetPreset(ctx.Key)); err != nil {
260274
return nil, err
261275
}
262276
dt := ctx.DigestType
@@ -544,7 +558,7 @@ func GetPublicKeyFromSignature(sig *compdesc.Signature, sctx signing.SigningCont
544558
return cert.PublicKey, nil
545559
}
546560

547-
func calculateReferenceDigests(state WalkingState, opts *Options, legacy bool) (rerr error) {
561+
func calculateReferenceDigests(cctx context.Context, state WalkingState, opts *Options, legacy bool) (rerr error) {
548562
var finalize finalizer.Finalizer
549563
defer finalize.FinalizeWithErrorPropagation(&rerr)
550564

@@ -568,7 +582,7 @@ func calculateReferenceDigests(state WalkingState, opts *Options, legacy bool) (
568582

569583
if nctx == nil || opts.Recursively || opts.Verify {
570584
digestOpts := opts.Nested()
571-
nctx, err = apply(state, nested, digestOpts, false)
585+
nctx, err = apply(cctx, state, nested, digestOpts, false)
572586
if err != nil {
573587
return errors.Wrap(err, refMsg(reference, "failed applying to component reference"))
574588
}
@@ -611,13 +625,18 @@ func calculateReferenceDigests(state WalkingState, opts *Options, legacy bool) (
611625
return nil
612626
}
613627

614-
func calculateResourceDigests(state WalkingState, cv ocm.ComponentVersionAccess, cd *compdesc.ComponentDescriptor, opts *Options, legacy bool, preset *metav1.NestedComponentDigests) (rerr error) {
628+
func calculateResourceDigests(cctx context.Context, state WalkingState, cv ocm.ComponentVersionAccess, cd *compdesc.ComponentDescriptor, opts *Options, legacy bool, preset *metav1.NestedComponentDigests) (rerr error) {
615629
var finalize finalizer.Finalizer
616630
defer finalize.FinalizeWithErrorPropagation(&rerr)
617631

618632
octx := cv.GetContext()
619633
blobdigesters := octx.BlobDigesters()
620634
for i, res := range cv.GetResources() {
635+
if err := common.IsContextCanceled(cctx); err != nil {
636+
opts.Printer.Printf("cancelled by caller\n")
637+
return err
638+
}
639+
621640
loop := finalize.Nested()
622641

623642
meta := res.Meta()

api/ocm/tools/signing/handler_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package signing_test
22

33
import (
4+
"context"
5+
46
. "github.com/mandelsoft/goutils/testutils"
57
. "github.com/onsi/ginkgo/v2"
68
. "github.com/onsi/gomega"
9+
"ocm.software/ocm/api/ocm/extensions/attrs/signingattr"
10+
common "ocm.software/ocm/api/utils/misc"
711

812
"ocm.software/ocm/api/ocm"
913
v1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
@@ -34,6 +38,31 @@ var _ = Describe("Simple signing handlers", func() {
3438
MustBeSuccessful(cv.SetResourceBlob(ocm.NewResourceMeta("blob", resourcetypes.PLAIN_TEXT, v1.LocalRelation), blobaccess.ForString(mime.MIME_TEXT, "test data"), "", nil))
3539
})
3640

41+
It("cancelled", func() {
42+
var opts signing.Options
43+
p, buf := common.NewBufferedPrinter()
44+
opts.Printer = p
45+
46+
opts.Eval(
47+
signing.SignatureName("signature"),
48+
signing.Update(),
49+
signing.Recursive(),
50+
signing.VerifyDigests(),
51+
52+
signing.PrivateKey("signature", priv),
53+
signing.SignerByAlgo(rsa.Algorithm),
54+
)
55+
56+
if opts.Signer == nil {
57+
opts.Signer = signingattr.Get(cv.GetContext()).GetSigner(rsa.Algorithm)
58+
}
59+
MustBeSuccessful(opts.Complete(cv.GetContext()))
60+
ctx, cancel := context.WithCancel(context.Background())
61+
cancel()
62+
ExpectError(signing.ApplyWithContext(ctx, nil, cv, &opts)).To(MatchError(context.Canceled))
63+
Expect(buf.String()).To(Equal("cancelled by caller\n"))
64+
})
65+
3766
DescribeTable("rsa handlers", func(kind string) {
3867
Must(signing.SignComponentVersion(cv, "signature", signing.PrivateKey("signature", priv), signing.SignerByAlgo(kind)))
3968
Must(signing.VerifyComponentVersion(cv, "signature", signing.PublicKey("signature", pub)))

api/ocm/tools/transfer/convenience.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package transfer
22

33
import (
4+
"context"
5+
46
"ocm.software/ocm/api/ocm"
57
common "ocm.software/ocm/api/utils/misc"
68
)
@@ -25,5 +27,23 @@ func Transfer(cv ocm.ComponentVersionAccess, tgt ocm.Repository, optlist ...Tran
2527
if err != nil {
2628
return err
2729
}
28-
return TransferWithHandler(local.printer, cv, tgt, h)
30+
return TransferVersionWithContext(common.WithPrinter(context.Background(), local.printer), nil, cv, tgt, h)
31+
}
32+
33+
// TransferWithContext uses the transfer handler based on the given options to control
34+
// the transfer process. The default handler is the standard handler.
35+
func TransferWithContext(ctx context.Context, cv ocm.ComponentVersionAccess, tgt ocm.Repository, optlist ...TransferOption) error {
36+
h, err := NewTransferHandler(optlist...)
37+
if err != nil {
38+
return err
39+
}
40+
var local localOptions
41+
err = local.Eval(optlist...)
42+
if err != nil {
43+
return err
44+
}
45+
if local.printer != nil {
46+
ctx = common.WithPrinter(ctx, local.printer)
47+
}
48+
return TransferVersionWithContext(ctx, nil, cv, tgt, h)
2949
}

api/ocm/tools/transfer/transfer.go

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package transfer
22

33
import (
4+
"context"
45
"fmt"
56

67
"github.com/mandelsoft/goutils/errors"
@@ -23,14 +24,23 @@ type WalkingState = common.WalkingState[*struct{}, interface{}]
2324
type TransportClosure = common.NameVersionInfo[*struct{}]
2425

2526
func TransferVersion(printer common.Printer, closure TransportClosure, src ocmcpi.ComponentVersionAccess, tgt ocmcpi.Repository, handler TransferHandler) error {
27+
return TransferVersionWithContext(common.WithPrinter(context.Background(), common.AssurePrinter(printer)), closure, src, tgt, handler)
28+
}
29+
30+
func TransferVersionWithContext(ctx context.Context, closure TransportClosure, src ocmcpi.ComponentVersionAccess, tgt ocmcpi.Repository, handler TransferHandler) error {
2631
if closure == nil {
2732
closure = TransportClosure{}
2833
}
2934
state := WalkingState{Closure: closure}
30-
return transferVersion(common.AssurePrinter(printer), Logger(src), state, src, tgt, handler)
35+
return transferVersion(ctx, Logger(src), state, src, tgt, handler)
3136
}
3237

33-
func transferVersion(printer common.Printer, log logging.Logger, state WalkingState, src ocmcpi.ComponentVersionAccess, tgt ocmcpi.Repository, handler TransferHandler) (rerr error) {
38+
func transferVersion(ctx context.Context, log logging.Logger, state WalkingState, src ocmcpi.ComponentVersionAccess, tgt ocmcpi.Repository, handler TransferHandler) (rerr error) {
39+
printer := common.GetPrinter(ctx)
40+
if err := common.IsContextCanceled(ctx); err != nil {
41+
printer.Printf("transfer cancelled by caller\n")
42+
return err
43+
}
3444
nv := common.VersionedElementKey(src)
3545
log = log.WithValues("history", state.History.String(), "version", nv)
3646
if ok, err := state.Add(ocm.KIND_COMPONENTVERSION, nv); !ok {
@@ -155,7 +165,6 @@ func transferVersion(printer common.Printer, log logging.Logger, state WalkingSt
155165
return errors.Wrapf(err, "%s: creating target version", state.History)
156166
}
157167

158-
subp := printer.AddGap(" ")
159168
list := errors.ErrListf("component references for %s", nv)
160169
log.Info(" transferring references")
161170
for _, r := range d.References {
@@ -164,7 +173,7 @@ func transferVersion(printer common.Printer, log logging.Logger, state WalkingSt
164173
return errors.Wrapf(err, "%s: nested component %s[%s:%s]", state.History, r.GetName(), r.ComponentName, r.GetVersion())
165174
}
166175
if cv != nil {
167-
list.Add(transferVersion(subp, log.WithValues("ref", r.Name), state, cv, tgt, shdlr))
176+
list.Add(transferVersion(common.AddPrinterGap(ctx, " "), log.WithValues("ref", r.Name), state, cv, tgt, shdlr))
168177
list.Addf(nil, cv.Close(), "closing reference %s", r.Name)
169178
}
170179
}
@@ -196,7 +205,7 @@ func transferVersion(printer common.Printer, log logging.Logger, state WalkingSt
196205
// corrupted content in target.
197206
// If no copy is done, merge must keep the access methods in target!!!
198207
if !doMerge || doCopy {
199-
err = copyVersion(printer, log, state.History, src, t, n, handler)
208+
err = copyVersion(ctx, printer, log, state.History, src, t, n, handler)
200209
if err != nil {
201210
return err
202211
}
@@ -212,11 +221,15 @@ func transferVersion(printer common.Printer, log logging.Logger, state WalkingSt
212221
}
213222

214223
func CopyVersion(printer common.Printer, log logging.Logger, hist common.History, src ocm.ComponentVersionAccess, t ocm.ComponentVersionAccess, handler TransferHandler) (rerr error) {
215-
return copyVersion(common.AssurePrinter(printer), log, hist, src, t, src.GetDescriptor().Copy(), handler)
224+
return copyVersion(context.Background(), common.AssurePrinter(printer), log, hist, src, t, src.GetDescriptor().Copy(), handler)
225+
}
226+
227+
func CopyVersionWithContext(cctx context.Context, log logging.Logger, hist common.History, src ocm.ComponentVersionAccess, t ocm.ComponentVersionAccess, handler TransferHandler) (rerr error) {
228+
return copyVersion(cctx, common.GetPrinter(cctx), log, hist, src, t, src.GetDescriptor().Copy(), handler)
216229
}
217230

218231
// copyVersion (purely internal) expects an already prepared target comp desc for t given as prep.
219-
func copyVersion(printer common.Printer, log logging.Logger, hist common.History, src ocm.ComponentVersionAccess, t ocm.ComponentVersionAccess, prep *compdesc.ComponentDescriptor, handler TransferHandler) (rerr error) {
232+
func copyVersion(cctx context.Context, printer common.Printer, log logging.Logger, hist common.History, src ocm.ComponentVersionAccess, t ocm.ComponentVersionAccess, prep *compdesc.ComponentDescriptor, handler TransferHandler) (rerr error) {
220233
var finalize finalizer.Finalizer
221234

222235
defer errors.PropagateError(&rerr, finalize.Finalize)
@@ -232,6 +245,11 @@ func copyVersion(printer common.Printer, log logging.Logger, hist common.History
232245
for i, r := range src.GetResources() {
233246
var m ocmcpi.AccessMethod
234247

248+
if err := common.IsContextCanceled(cctx); err != nil {
249+
printer.Printf("cancelled by caller\n")
250+
return err
251+
}
252+
235253
nested := finalize.Nested()
236254

237255
a, err := r.Access()
@@ -295,6 +313,11 @@ func copyVersion(printer common.Printer, log logging.Logger, hist common.History
295313
for i, r := range src.GetSources() {
296314
var m ocmcpi.AccessMethod
297315

316+
if err := common.IsContextCanceled(cctx); err != nil {
317+
printer.Printf("cancelled by caller\n")
318+
return err
319+
}
320+
298321
a, err := r.Access()
299322
if err == nil {
300323
m, err = a.AccessMethod(src)

api/ocm/tools/transfer/transferhandler/standard/handler_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package standard_test
22

33
import (
4+
"context"
45
"encoding/json"
56
"fmt"
67

@@ -109,6 +110,26 @@ var _ = Describe("Transfer handler", func() {
109110
env.Cleanup()
110111
})
111112

113+
It("cancelled", func() {
114+
src := Must(ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env))
115+
defer Close(src, "source")
116+
cv := Must(src.LookupComponentVersion(COMPONENT, VERSION))
117+
defer Close(cv, "source cv")
118+
tgt := Must(ctf.Create(env.OCMContext(), accessobj.ACC_WRITABLE|accessobj.ACC_CREATE, OUT, 0o700, accessio.FormatDirectory, env))
119+
defer Close(tgt, "target")
120+
121+
// handler, err := standard.New(standard.ResourcesByValue())
122+
p, buf := common.NewBufferedPrinter()
123+
opts := []transferhandler.TransferOption{standard.ResourcesByValue(), &optionsChecker{}}
124+
125+
ctx, cancel := context.WithCancel(context.Background())
126+
ctx = common.WithPrinter(ctx, p)
127+
cancel()
128+
ExpectError(transfer.TransferWithContext(ctx, cv, tgt, opts...)).To(MatchError(context.Canceled))
129+
130+
Expect(buf.String()).To(Equal("transfer cancelled by caller\n"))
131+
})
132+
112133
It("test", func() {
113134
src := Must(ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env))
114135
defer Close(src, "source")

api/utils/misc/context.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package misc
2+
3+
import (
4+
"context"
5+
"reflect"
6+
)
7+
8+
var printerKey = reflect.TypeOf(printer{})
9+
10+
func WithPrinter(ctx context.Context, printer Printer) context.Context {
11+
return context.WithValue(ctx, printerKey, AssurePrinter(printer))
12+
}
13+
14+
func GetPrinter(ctx context.Context) Printer {
15+
p := ctx.Value(printerKey)
16+
if p == nil {
17+
return NonePrinter
18+
}
19+
return p.(Printer)
20+
}
21+
22+
func AddPrinterGap(ctx context.Context, gap string) context.Context {
23+
return WithPrinter(ctx, GetPrinter(ctx).AddGap(gap))
24+
}
25+
26+
func IsContextCanceled(ctx context.Context) error {
27+
select {
28+
case <-ctx.Done():
29+
return ctx.Err()
30+
default:
31+
return nil
32+
}
33+
}

0 commit comments

Comments
 (0)