Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Benchmarking #2192

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@ feature_%: ## Run acceptance tests for a single feature file, e.g. make feature_
scenario_%: build ## Run acceptance tests for a single scenario, e.g. make scenario_inline_policy
@cd acceptance && go test -test.run 'TestFeatures/$*'

benchmark_%:
cd benchmark/$*
go run . -benchnum 10

.PHONY: benchmark
benchmark: benchmark_simple ## Run benchmarks

.PHONY: ci
ci: test lint-fix acceptance ## Run the usual required CI tasks

Expand Down
12 changes: 12 additions & 0 deletions benchmark/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Benchmarks of ec CLI

Benchmarks within this directory use the [golang
benchmarking](golang.org/x/benchmarks/) package and output in the [standard
benchmark
format](https://go.googlesource.com/proposal/+/master/design/14313-benchmark-format.md).

Each benchmark is built as a standalone executable with no external dependency
other than any data that is contained within it. Benchmarks are run from within
the directory they're defined in, simply by running `go run .`, additional
arguments can be passed in, for example `-benchnum 10` to run the benchmark 10
times.
50 changes: 50 additions & 0 deletions benchmark/internal/registry/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright The Enterprise Contract Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

package registry

import (
"context"

"github.com/testcontainers/testcontainers-go/modules/registry"
)

type Closer interface {
Close()
}

type registryCloser struct {
container *registry.RegistryContainer
}

func (r *registryCloser) Close() {
if r == nil || r.container == nil {
return
}

Check warning on line 36 in benchmark/internal/registry/registry.go

View check run for this annotation

Codecov / codecov/patch

benchmark/internal/registry/registry.go#L33-L36

Added lines #L33 - L36 were not covered by tests

_ = r.container.Terminate(context.Background())

Check warning on line 38 in benchmark/internal/registry/registry.go

View check run for this annotation

Codecov / codecov/patch

benchmark/internal/registry/registry.go#L38

Added line #L38 was not covered by tests
}

func Launch(data string) (string, Closer, error) {
ctx := context.Background()
r, err := registry.Run(ctx, "registry:2.8.3", registry.WithData(data))
c := &registryCloser{r}
if err != nil {
return "", c, err
}

Check warning on line 47 in benchmark/internal/registry/registry.go

View check run for this annotation

Codecov / codecov/patch

benchmark/internal/registry/registry.go#L41-L47

Added lines #L41 - L47 were not covered by tests

return r.RegistryName, c, nil

Check warning on line 49 in benchmark/internal/registry/registry.go

View check run for this annotation

Codecov / codecov/patch

benchmark/internal/registry/registry.go#L49

Added line #L49 was not covered by tests
}
32 changes: 32 additions & 0 deletions benchmark/internal/suite/suite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright The Enterprise Contract Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

package suite

import (
"io"

"github.com/enterprise-contract/ec-cli/cmd"
"github.com/enterprise-contract/ec-cli/cmd/root"
)

func Execute(args []string) error {
c := root.NewRootCmd()
cmd.AddCommandsTo(c)
c.SetArgs(args)
c.SetOutput(io.Discard)
return c.Execute()

Check warning on line 31 in benchmark/internal/suite/suite.go

View check run for this annotation

Codecov / codecov/patch

benchmark/internal/suite/suite.go#L26-L31

Added lines #L26 - L31 were not covered by tests
}
8 changes: 8 additions & 0 deletions benchmark/offliner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Offliner

A tool to offline, i.e. place all data for an container image in a remote
registry to a local data directory. This local data directory can be mounted to
docker.io/registry container registry so running against it eliminates remote
network access.

Use: `offliner <pinned image reference> <data directory>`
213 changes: 213 additions & 0 deletions benchmark/offliner/offliner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
// Copyright The Enterprise Contract Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

package main

import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"strings"

"github.com/docker/docker/api/types/container"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/logs"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/registry"
)

func items(sourceImages []name.Reference, destination name.Registry) map[string]string {
items := map[string]string{}
for _, sourceImage := range sourceImages {
var dest fmt.Stringer = destination.Repo(sourceImage.Context().RepositoryStr())
if d, ok := sourceImage.(name.Digest); ok {
dest = dest.(name.Repository).Digest(d.DigestStr())
}
if t, ok := sourceImage.(name.Tag); ok {
dest = dest.(name.Repository).Tag(t.TagStr())
}

items[sourceImage.String()] = dest.String()
}

return items
}

func digestTag(ref name.Reference, suffix string) name.Reference {
digest := ref.(name.Digest).DigestStr()

tag := fmt.Sprintf("%s.%s", strings.Replace(digest, ":", "-", 1), suffix)

return ref.Context().Tag(tag)

Check warning on line 59 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L54-L59

Added lines #L54 - L59 were not covered by tests
}

func signature(ref name.Reference) name.Reference {
return digestTag(ref, "sig")

Check warning on line 63 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L62-L63

Added lines #L62 - L63 were not covered by tests
}

func attestation(ref name.Reference) name.Reference {
return digestTag(ref, "att")

Check warning on line 67 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L66-L67

Added lines #L66 - L67 were not covered by tests
}

func sbom(ref name.Reference) name.Reference {
return digestTag(ref, "sbom")

Check warning on line 71 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L70-L71

Added lines #L70 - L71 were not covered by tests
}

func referrers(ref name.Reference) ([]name.Reference, error) {
var err error
var idx v1.ImageIndex
if d, ok := ref.(name.Digest); ok {
idx, err = remote.Referrers(d)

if err != nil {
return nil, err
}
} else {
img, err := remote.Image(ref)
if err != nil {
return nil, err
}

Check warning on line 87 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L74-L87

Added lines #L74 - L87 were not covered by tests

digest, err := img.Digest()
if err != nil {
return nil, err
}
ref := ref.Context().Digest(digest.String())
idx, err = remote.Referrers(ref)
if err != nil {
return nil, err
}

Check warning on line 97 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L89-L97

Added lines #L89 - L97 were not covered by tests
}

indexManifest, err := idx.IndexManifest()
if err != nil {
return nil, err
}

Check warning on line 103 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L100-L103

Added lines #L100 - L103 were not covered by tests

references := make([]name.Reference, 0, len(indexManifest.Manifests))
for _, manifest := range indexManifest.Manifests {
r := ref.Context().Digest(manifest.Digest.String())
references = append(references, r)
}

Check warning on line 109 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L105-L109

Added lines #L105 - L109 were not covered by tests

return references, nil

Check warning on line 111 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L111

Added line #L111 was not covered by tests
}

func related(ref name.Reference) []name.Reference {
references := []name.Reference{ref}

for _, r := range []name.Reference{
signature(ref),
attestation(ref),
sbom(ref),
} {
if _, err := remote.Image(r); err == nil {
references = append(references, r)
}

Check warning on line 124 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L114-L124

Added lines #L114 - L124 were not covered by tests
}

refs, err := referrers(ref)
if err != nil {
return references
}

Check warning on line 130 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L127-L130

Added lines #L127 - L130 were not covered by tests

return append(references, refs...)

Check warning on line 132 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L132

Added line #L132 was not covered by tests
}

func main() {
if len(os.Args) != 3 {
fmt.Fprintf(os.Stderr, "Usage: %s <image> <directory>\n", os.Args[0])
os.Exit(1)
}

Check warning on line 139 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L135-L139

Added lines #L135 - L139 were not covered by tests

sourceImage := os.Args[1]
dir, err := filepath.Abs(os.Args[2])
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(2)
}

Check warning on line 146 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L141-L146

Added lines #L141 - L146 were not covered by tests

if _, err := os.Stat(dir); os.IsNotExist(err) {
if err := os.MkdirAll(dir, 0755); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(3)
}

Check warning on line 152 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L148-L152

Added lines #L148 - L152 were not covered by tests
}

source, err := name.ParseReference(sourceImage)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(4)
}

Check warning on line 159 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L155-L159

Added lines #L155 - L159 were not covered by tests

_, ok := source.(name.Digest)
if !ok {
fmt.Fprintln(os.Stderr, "use pinned image references")
os.Exit(5)
}

Check warning on line 165 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L161-L165

Added lines #L161 - L165 were not covered by tests

ctx := context.Background()
registry, err := registry.Run(ctx,
registry.DefaultImage,
testcontainers.WithConfigModifier(func(config *container.Config) {
config.User = fmt.Sprintf("%d:%d", os.Getuid(), os.Getgid())
}),
testcontainers.WithHostConfigModifier(func(hostConfig *container.HostConfig) {
hostConfig.UsernsMode = "host"
hostConfig.Binds = append(hostConfig.Binds, fmt.Sprintf("%s:/var/lib/registry:z", dir))
}),

Check warning on line 176 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L167-L176

Added lines #L167 - L176 were not covered by tests
)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(6)
}
defer registry.Terminate(ctx)

destination, err := name.NewRegistry(registry.RegistryName, name.Insecure)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(7)
}
logs.Progress = log.New(os.Stdout, "", log.LstdFlags)

sourceImages := related(source)

idx, err := remote.Index(source)
if err == nil {
idxManifest, err := idx.IndexManifest()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(8)
}

Check warning on line 199 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L178-L199

Added lines #L178 - L199 were not covered by tests

for _, m := range idxManifest.Manifests {
img := source.Context().Digest(m.Digest.String())
sourceImages = append(sourceImages, related(img)...)
}

Check warning on line 204 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L201-L204

Added lines #L201 - L204 were not covered by tests
}

for s, d := range items(sourceImages, destination) {
if err := crane.Copy(s, d); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(9)
}

Check warning on line 211 in benchmark/offliner/offliner.go

View check run for this annotation

Codecov / codecov/patch

benchmark/offliner/offliner.go#L207-L211

Added lines #L207 - L211 were not covered by tests
}
}
49 changes: 49 additions & 0 deletions benchmark/offliner/offliner_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright The Enterprise Contract Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

package main

import (
"maps"
"testing"

"github.com/google/go-containerregistry/pkg/name"
)

func TestItems(t *testing.T) {
dest, err := name.NewRegistry("newregistry.io")
if err != nil {
t.Fatal(err)
}

imgs := []name.Reference{
name.MustParseReference("registry.io/repository/image"),
name.MustParseReference("registry.io/repository/image:tag"),
name.MustParseReference("registry.io/repository/image@sha256:9f80b4f7506d2799298a685162723482cac160abf701e029ee5cbaa6c74967ea"),
}

got := items(imgs, dest)

expected := map[string]string{
"registry.io/repository/image": "newregistry.io/repository/image:latest",
"registry.io/repository/image:tag": "newregistry.io/repository/image:tag",
"registry.io/repository/image@sha256:9f80b4f7506d2799298a685162723482cac160abf701e029ee5cbaa6c74967ea": "newregistry.io/repository/image@sha256:9f80b4f7506d2799298a685162723482cac160abf701e029ee5cbaa6c74967ea",
}

if !maps.Equal(expected, got) {
t.Errorf("expected and got images differ: %v != %v", expected, got)
}
}
1 change: 1 addition & 0 deletions benchmark/simple/data/git/rhtap-ec-policy.git
Submodule rhtap-ec-policy.git added at a524ee
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.image.config.v1+json","size":342,"digest":"sha256:2b5c9030c3dc80a3cc7c1e2f6454b9b8dc534f5ef4594b1264d8999adbb9acdc"},"layers":[{"mediaType":"application/vnd.dev.cosign.simplesigning.v1+json","size":301,"digest":"sha256:d1317b7f65c749d2d588bc8df2ee25d35f0e6049f8f988d8a12e9761eaede92f","annotations":{"dev.cosignproject.cosign/signature":"MEUCIF8OHzZbAgy8LcWzaOfDdFhngP3Dc1L219CKzCP5p0/DAiEA5ZLbmY0b0YwdpBIekcgs5ZhjL58vl4Z5UaIc7RfB1+s=","dev.sigstore.cosign/certificate":"","dev.sigstore.cosign/chain":""}},{"mediaType":"application/vnd.dev.cosign.simplesigning.v1+json","size":301,"digest":"sha256:d1317b7f65c749d2d588bc8df2ee25d35f0e6049f8f988d8a12e9761eaede92f","annotations":{"dev.cosignproject.cosign/signature":"MEYCIQD36/rc9PNzQfxmQxCNBurLUE3l7H897d9TQxL44TpM/AIhAPze0wYsIcYNfD9kL115AXVz+Pm5H0ecUMYCyOeQzNN6","dev.sigstore.cosign/certificate":"","dev.sigstore.cosign/chain":""}}]}
Loading
Loading