Skip to content

Commit

Permalink
Move patch tests to go integration tests
Browse files Browse the repository at this point in the history
Co-authored-by: Sertaç Özercan <[email protected]>
Signed-off-by: Brian Goff <[email protected]>
  • Loading branch information
cpuguy83 and sozercan committed Aug 14, 2023
1 parent 50b9bf9 commit 57c66e8
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 102 deletions.
25 changes: 12 additions & 13 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,22 @@ jobs:
name: copa_edge_linux_amd64.tar.gz
path: dist/linux_amd64/release/copa_edge_linux_amd64.tar.gz
- name: Load test cases for patch testing
id: load-tests
id: load-test-envs-matrix
shell: bash
run: echo "include=$(.github/workflows/generate-matrix.sh)" | tee -a "${GITHUB_OUTPUT}"
run: echo "buildkitenvs=$(.github/workflows/scripts/buildkit-env-matrix.sh)" | tee -a "${GITHUB_OUTPUT}"
outputs:
include: ${{ steps.load-tests.outputs.include }}
buildkitenvs: ${{ steps.load-test-envs-matrix.outputs.buildkitenvs }}

test-patch:
needs: build
name: Test patch ${{matrix.mode }} ${{ matrix.image }}:${{ matrix.tag }}
name: Test patch ${{ matrix.buildkit_mode }}
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 30
permissions: read-all
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.build.outputs.include) }}
buildkit_mode: ${{fromJson(needs.build.outputs.buildkitenvs)}}
steps:
- name: Download copa from build artifacts
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
Expand All @@ -104,7 +104,7 @@ jobs:
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Install required tools
shell: bash
run: ./scripts/ci/download-tooling.sh
run: .github/workflows/scripts/download-tooling.sh
- name: Download copa from build artifacts
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with:
Expand All @@ -115,13 +115,12 @@ jobs:
tar xzf copa_edge_linux_amd64.tar.gz
./copa --version
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2.2.0
- name: Run functional test
shell: bash
run: |
echo "[INFO]: Patching ${{ matrix.distro }} image with: ${{ matrix.description}}"
COPA="$(pwd)/copa" ./scripts/ci/run-functional-tests.sh
env:
TEST_BUILDKIT_MODE: ${{ matrix.mode }}
IMAGE_REF: ${{ matrix.image }}:${{ matrix.tag }}@${{ matrix.digest }}
set -eu -o pipefail
. .github/workflows/scripts/buildkitenvs/${{ matrix.buildkit_mode}}
go test -v ./integration --addr="${COPA_BUILDKIT_ADDR}" --copa="$(pwd)/copa"
51 changes: 0 additions & 51 deletions .github/workflows/generate-matrix.sh

This file was deleted.

17 changes: 17 additions & 0 deletions .github/workflows/scripts/buildkit-env-matrix.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash

set -eu -o pipefail

SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"

# Collect all the modes into a bash array
modes=()
for i in ${SCRIPT_DIR}/buildkitenvs/*; do
base="${i##*/}"
for j in "${i}"/*; do
modes+=(${base}/${j##*/})
done
done

# Convert bash array to json
jq -c --null-input '$ARGS.positional' --args -- "${modes[@]}"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
25 changes: 25 additions & 0 deletions integration/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package integration

import (
"flag"
"os"
"testing"
)

var (
buildkitAddr string
copaPath string
)

func TestMain(m *testing.M) {
flag.StringVar(&buildkitAddr, "addr", "", "buildkit address to pass through to copa binary")
flag.StringVar(&copaPath, "copa", "./copa", "path to copa binary")
flag.Parse()

if copaPath == "" {
panic("missing --copa")
}

ec := m.Run()
os.Exit(ec)
}
152 changes: 152 additions & 0 deletions integration/patch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package integration

import (
_ "embed"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"testing"

"github.com/opencontainers/go-digest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var (
//go:embed fixtures/test-images.json
testImages []byte

//go:embed fixtures/trivy_ignore.rego
trivyIgnore []byte
)

type testImage struct {
Image string `json:"image"`
Tag string `json:"tag"`
Distro string `json:"distro"`
Digest digest.Digest `json:"digest"`
Description string `json:"description"`
}

func TestPatch(t *testing.T) {
var images []testImage
err := json.Unmarshal(testImages, &images)
require.NoError(t, err)

tmp := t.TempDir()
ignoreFile := filepath.Join(tmp, "ignore.rego")
err = os.WriteFile(ignoreFile, trivyIgnore, 0o600)
require.NoError(t, err)

for _, img := range images {
img := img
t.Run(img.Description, func(t *testing.T) {
t.Parallel()

dir := t.TempDir()
output := filepath.Join(dir, "output.json")
ref := fmt.Sprintf("%s:%s@%s", img.Image, img.Tag, img.Digest)
tagPatched := img.Tag + "-patched"
patchedRef := fmt.Sprintf("%s:%s", img.Image, tagPatched)

t.Log("scanning original image")
scanner().
withIgnoreFile(ignoreFile).
withOutput(output).
// Do not set a non-zero exit code because we are expecting vulnerabilities.
scan(t, ref)

t.Log("patching image")
patch(t, ref, tagPatched, output)

t.Log("scanning patched image")
scanner().
withIgnoreFile(ignoreFile).
withSkipDBUpdate().
// here we want a non-zero exit code because we are expecting no vulnerabilities.
withExitCode(1).
scan(t, patchedRef)
})
}
}

func patch(t *testing.T, ref, patchedTag, scan string) {
var addrFl string
if buildkitAddr != "" {
addrFl = "-a=" + buildkitAddr
}

//#nosec G204
cmd := exec.Command(
copaPath,
"patch",
"-i="+ref,
"-t="+patchedTag,
"-r="+scan,
"--timeout=20m",
addrFl,
)
out, err := cmd.CombinedOutput()
require.NoError(t, err, string(out))
}

func scanner() *scannerCmd {
return &scannerCmd{}
}

type scannerCmd struct {
output string
skipDBUpdate bool
ignoreFile string
exitCode int
}

func (s *scannerCmd) scan(t *testing.T, ref string) {
args := []string{
"trivy",
"image",
"--vuln-type=os",
"--ignore-unfixed",
"--scanners=vuln",
}
if s.output != "" {
args = append(args, []string{"-o=" + s.output, "-f=json"}...)
}
if s.skipDBUpdate {
args = append(args, "--skip-db-update")
}
if s.ignoreFile != "" {
args = append(args, "--ignore-policy="+s.ignoreFile)
}
if s.exitCode != 0 {
args = append(args, "--exit-code="+strconv.Itoa(s.exitCode))
}

args = append(args, ref)

out, err := exec.Command(args[0], args[1:]...).CombinedOutput() //#nosec G204
assert.NoError(t, err, string(out))
}

func (s *scannerCmd) withOutput(p string) *scannerCmd {
s.output = p
return s
}

func (s *scannerCmd) withSkipDBUpdate() *scannerCmd {
s.skipDBUpdate = true
return s
}

func (s *scannerCmd) withIgnoreFile(p string) *scannerCmd {
s.ignoreFile = p
return s
}

func (s *scannerCmd) withExitCode(code int) *scannerCmd {
s.exitCode = code
return s
}
38 changes: 0 additions & 38 deletions scripts/ci/run-functional-tests.sh

This file was deleted.

0 comments on commit 57c66e8

Please sign in to comment.