Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ func (c *cli) newRunCmd() *cobra.Command {
locker := c.newLocker()

log.Info("running command")
out, err := locker.Run(c.cmd.Context(), args[0], args[1])
err := locker.Run(c.cmd.Context(), args[0], args[1])
if err != nil {
if errors.Is(err, lock.ErrLocked) {
log.Info("did not run command. key is locked")
return
}

log.Fatalw("command failed", "error", err, "output", out)
log.Fatalw("command failed", "error", err)
}

log.Infow("command succeeded", "output", out)
log.Infow("command succeeded")
},
}

Expand Down
17 changes: 13 additions & 4 deletions lock/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package lock
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"time"
Expand All @@ -11,17 +12,25 @@ import (
// Run acquires a lock under the specified key, executes the command, and then unlocks the key.
// Returns ErrLocked if the key is already locked. Otherwise returns combined stdout and stderr
// of the command and the command error. If the unlock step fails the lock expires after 24 hours.
func (c *Client) Run(ctx context.Context, key, command string) (string, error) {
func (c *Client) Run(ctx context.Context, key, command string) error {
// use context.Background here so that unlock runs even if the context is cancelled
defer c.Unlock(context.Background(), key) //nolint:errcheck

err := c.Lock(ctx, key, time.Hour*24) //nolint:gomnd
if err != nil {
return "", fmt.Errorf("lock failed: %w", err)
return fmt.Errorf("lock failed: %w", err)
}

// Build command
fields := strings.Fields(command)
cmdout, cmderr := exec.CommandContext(ctx, fields[0], fields[1:]...).CombinedOutput() //nolint:gosec
cmd := exec.CommandContext(ctx, fields[0], fields[1:]...) //nolint:gosec

return strings.TrimSpace(string(cmdout)), cmderr
// Write to std outputs
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

if err := cmd.Run(); err != nil {
return fmt.Errorf("command failed: %w", err)
}
return nil
}
9 changes: 3 additions & 6 deletions lock/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,17 @@ func TestRun(t *testing.T) {

wg.Add(1)
go func() {
out, err := tc.Run(ctx, "locktest", "sleep 1")
assert.Empty(t, out)
err := tc.Run(ctx, "locktest", "sleep 1")
assert.NoError(t, err)
wg.Done()
}()
time.Sleep(time.Millisecond * 500)

out, err := tc.Run(ctx, "locktest", "sleep 5")
assert.Empty(t, out)
err := tc.Run(ctx, "locktest", "sleep 5")
assert.ErrorIs(t, err, ErrLocked)

out, err = tc.Run(ctx, "locktest", "echo hello test")
err = tc.Run(ctx, "locktest", "echo hello test")
assert.NoError(t, err)
assert.Equal(t, "hello test", out)

wg.Wait()
}