diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ee4e6a..1ac6d8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,9 @@ on: pull_request: branches: [ main ] +permissions: + contents: read + jobs: checks: @@ -19,82 +22,60 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} - - - name: Set env - shell: bash - run: | - echo "GOPATH=${{ github.workspace }}" >> $GITHUB_ENV - echo "${{ github.workspace }}/bin" >> $GITHUB_PATH - - - uses: actions/checkout@v3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - path: src/github.com/containerd/go-runc fetch-depth: 25 - - uses: containerd/project-checks@d7751f3c375b8fe4a84c02a068184ee4c1f59bc4 # v1.2.2 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - working-directory: src/github.com/containerd/go-runc + go-version: ${{ matrix.go-version }} + + - uses: thaJeztah/project-checks@774919130d95adbf99c948b88cc0bde31badccfa # https://github.com/containerd/project-checks/pull/25 linters: name: Linters runs-on: ${{ matrix.os }} timeout-minutes: 10 + permissions: + contents: read + checks: write # allow the action to annotate code in the PR. strategy: matrix: - go-version: [1.21.x] + go-version: [stable] os: [ubuntu-22.04] steps: - - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: actions/checkout@v3 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - path: src/github.com/containerd/go-runc - - - name: Set env - shell: bash - run: | - echo "GOPATH=${{ github.workspace }}" >> $GITHUB_ENV - echo "${{ github.workspace }}/bin" >> $GITHUB_PATH + go-version: ${{ matrix.go-version }} - - uses: golangci/golangci-lint-action@v3 + - uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 with: - version: v1.51.1 - working-directory: src/github.com/containerd/go-runc - args: --timeout=5m + version: v2.13 tests: - name: Tests + name: Tests (Go ${{ matrix.go-version || 'minimum' }}) runs-on: ${{ matrix.os }} timeout-minutes: 5 strategy: + fail-fast: false matrix: - go-version: [1.21.x, 1.22.x] + go-version: + - "" # leave empty to use go-version-file (use go.mod) + - oldstable + - stable os: [ubuntu-22.04] steps: - - uses: actions/checkout@v3 - with: - path: src/github.com/containerd/go-runc - - - uses: actions/setup-go@v3 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ matrix.go-version }} - - - name: Set env - shell: bash - run: | - echo "GOPATH=${{ github.workspace }}" >> $GITHUB_ENV - echo "${{ github.workspace }}/bin" >> $GITHUB_PATH - + go-version-file: go.mod # used when go-version is empty - run: | go test -v -race -covermode=atomic -coverprofile=coverage.txt ./... bash <(curl -s https://codecov.io/bash) - working-directory: src/github.com/containerd/go-runc diff --git a/.golangci.yml b/.golangci.yml index 240eaed..21383cc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,20 +1,27 @@ +version: "2" + linters: enable: - - gofmt - - goimports + - gosec + - govet - ineffassign - misspell - revive - staticcheck - unconvert - unused - - vet disable: - errcheck + exclusions: + presets: + - common-false-positives + - legacy + - std-error-handling -issues: - include: - - EXC0002 +formatters: + enable: + - gofmt + - goimports run: - timeout: 2m + timeout: 5m diff --git a/command_linux.go b/command_linux.go index 9bc94c8..9b87131 100644 --- a/command_linux.go +++ b/command_linux.go @@ -33,7 +33,7 @@ func (r *Runc) commandWithCustomLogFile(context context.Context, logFile string, if command == "" { command = DefaultCommand } - cmd := exec.CommandContext(context, command, append(r.args(logFile), args...)...) + cmd := exec.CommandContext(context, command, append(r.args(logFile), args...)...) // #nosec G702 -- executing the caller-configured runtime is the purpose of this package. cmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: r.Setpgid, } diff --git a/command_other.go b/command_other.go index a9045b2..a2b22b1 100644 --- a/command_other.go +++ b/command_other.go @@ -33,7 +33,7 @@ func (r *Runc) commandWithCustomLogFile(context context.Context, logFile string, if command == "" { command = DefaultCommand } - cmd := exec.CommandContext(context, command, append(r.args(logFile), args...)...) + cmd := exec.CommandContext(context, command, append(r.args(logFile), args...)...) // #nosec G702 -- executing the caller-configured runtime is the purpose of this package. cmd.Env = append(os.Environ(), extraEnv(context)...) cmd.Dir = r.WorkDir return cmd diff --git a/monitor.go b/monitor.go index b9938ad..ef4936d 100644 --- a/monitor.go +++ b/monitor.go @@ -112,7 +112,7 @@ func (m *defaultMonitor) StartLocked(c *exec.Cmd) (chan Exit, error) { return ec, nil } -func (m *defaultMonitor) Wait(c *exec.Cmd, ec chan Exit) (int, error) { +func (m *defaultMonitor) Wait(_ *exec.Cmd, ec chan Exit) (int, error) { e := <-ec return e.Status, nil } diff --git a/runc.go b/runc.go index b63a4e7..b2e61c3 100644 --- a/runc.go +++ b/runc.go @@ -14,6 +14,10 @@ limitations under the License. */ +// Package runc provides Go bindings for invoking and interacting with the +// [runc] command-line interface. +// +// [runc]: https://github.com/opencontainers/runc package runc import ( diff --git a/runc_test.go b/runc_test.go index 5329e23..c44b940 100644 --- a/runc_test.go +++ b/runc_test.go @@ -90,25 +90,28 @@ spec: nope }) } +// TestParallelCmds exercises concurrent callers of cmdOutput and is primarily +// intended to detect unsafe shared state when run with the race detector. func TestParallelCmds(t *testing.T) { - rc := &Runc{ - // we don't need a real runc, we just want to test running a caller of cmdOutput in parallel - Command: "/bin/true", - } - var wg sync.WaitGroup + // we don't need a real runc, we just want to test running a caller of cmdOutput in parallel + rc := &Runc{Command: "/bin/true"} - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + var wg sync.WaitGroup + start := make(chan struct{}) for i := 0; i < 256; i++ { wg.Add(1) go func() { defer wg.Done() - // We just want to fail if there is a race condition detected by - // "-race", so we ignore the (expected) error here. - _, _ = rc.Version(ctx) + <-start + + if _, err := rc.Version(context.Background()); err != nil { + t.Error("expected parsing an empty version to pass") + } }() } + + close(start) wg.Wait() } diff --git a/socket.go b/socket.go index 5b97dd2..e0f6805 100644 --- a/socket.go +++ b/socket.go @@ -64,13 +64,13 @@ func newTempSocket(prefix, name string) (*Socket, error) { } s, err := newSocket(filepath.Join(dir, name)) if err != nil { - os.RemoveAll(dir) + _ = os.RemoveAll(dir) // #nosec G703 -- dir was created by os.MkdirTemp above. return nil, err } s.rmdir = true if runtimeDir != "" { - if err := os.Chmod(s.Path(), 0o755|os.ModeSticky); err != nil { - s.Close() + if err := os.Chmod(s.Path(), 0o755|os.ModeSticky); err != nil { // #nosec G703 -- the path identifies the socket created in the temporary directory. + _ = s.Close() return nil, err } } @@ -86,8 +86,8 @@ func (c *Socket) Path() string { func (c *Socket) Close() error { err := c.l.Close() if c.rmdir { - if rerr := os.RemoveAll(filepath.Dir(c.Path())); err == nil { - err = rerr + if rErr := os.RemoveAll(filepath.Dir(c.Path())); err == nil { // #nosec G703 -- rmdir is set only for sockets created in a private temporary directory. + err = rErr } } return err