#Error handling, cleaning up, and unwinding (WithDefer?)

1 messages · Page 1 of 1 (latest)

umbral plume
#

I have another error handling situation that I haven't figured out yet.

  1. build a CLI in dagger
  2. boot a VM (or anything outside of dagger)
  3. copy CLI to VM
  4. run CLI tests on VM
  5. delete the VM (cleanup external resources)

The problem is that I don't know when the pipeline failed and if I need to clean up. If I fail in 4, I need to cleanup, if I fail in 1, I do not need to.

There used to be an idea for dagger down that would have the reverse of some steps. Maybe it would be better to have something like WithDefer, which creates an unwind stack? Perhaps this could have conditions for running like pass, fail, always?

umbral plume
umbral plume
#

seems not, I think what I am after is a hook that happens as the step would happen when running. They seem to be evaluated up to a sync point, so a boolean value is quickly set and then unset, probably before the graphql call to the engine?

warm meadow
#

With python for example I’d just capture an exception at the right step and cleanup there. In Go you handle the error but it’s the same.

umbral plume
#

except the error is not thrown from where you define the container

#

you only get errors when you call Sync, Stdout, Stderr, ExitCode

#

but which WithExec failed when the pipeline is actually evaluated?

warm meadow
#

You know which one from the error that’s returned. The cmd is returned in that error.

light sandal
#

@umbral plume even though I agree with you that this could use a better DX, this is a way I thought this could be implemented today using With

func main() {
    ctx := context.Background()
    client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
    if err != nil {
        panic(err)
    }
    defer client.Close()

    rb := client.Pipeline("rollback").Container().
        From("alpine")

    _, err = client.Pipeline("test").
        Container().
        From("alpine").
        With(func(c *dagger.Container) *dagger.Container {
            c, werr := c.WithExec([]string{"apk", "add", "curll"}).Sync(ctx)
            if werr != nil {
                rb.WithExec([]string{"whatever"})
            }
            return c
        }).Sync(ctx)

    if err != nil {
        // pipeline failed, run rb pipeline
        rb.Sync(ctx)
    }
}

^ basically checking on each step if there was an error and keeping a parallel rollback DAG

umbral plume
#

yeah, I thought you might need to wrap everything in Sync's

#

I'm doing it in groups now, because I don't actually care about the exact step right now, just a few spans

#

func WithError(func (*Container) (*Container, error)) might be useful as well

light sandal
umbral plume
#

yeah, so

  1. a way to build up an unroll stack
  2. a way to attach error handlers to a step or named pipeline
light sandal
#

👍 I'll try to open an issue during the weekend

umbral plume
#

cool, thanks!