Skip to content

Commit

Permalink
Merge pull request #1046 from SaschaSchwarze0/sascha-shipwright-manag…
Browse files Browse the repository at this point in the history
…ed-push

SHIP-0026 Shipwright-managed push
  • Loading branch information
openshift-merge-robot authored Apr 18, 2023
2 parents 968c175 + cde42a5 commit 815218b
Show file tree
Hide file tree
Showing 114 changed files with 3,935 additions and 2,713 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ jobs:
run: |
export TEST_NAMESPACE=shipwright-build
export TEST_IMAGE_REPO=registry.registry.svc.cluster.local:32222/shipwright-io/build-e2e
export TEST_IMAGE_REPO_INSECURE=true
export TEST_E2E_TIMEOUT_MULTIPLIER=2
make test-e2e
- name: Build controller logs
Expand Down
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ TEST_E2E_TIMEOUT_MULTIPLIER ?= 1

# test repository to store images build during end-to-end tests
TEST_IMAGE_REPO ?= ghcr.io/shipwright-io/build/build-e2e
# test container registyr secret name
# test repository insecure (HTTP or self-signed certificate)
TEST_IMAGE_REPO_INSECURE ?= false
# test container registry secret name
TEST_IMAGE_REPO_SECRET ?=
# test container registry secret, must be defined during runtime
TEST_IMAGE_REPO_DOCKERCONFIGJSON ?=
Expand Down Expand Up @@ -153,7 +155,7 @@ install-counterfeiter:
# Install golangci-lint via: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
.PHONY: sanity-check
sanity-check:
golangci-lint run
golangci-lint run --timeout=10m

# https://github.com/shipwright-io/build/issues/123
.PHONY: test
Expand Down
117 changes: 14 additions & 103 deletions cmd/bundle/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ import (
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"

"github.com/docker/cli/cli/config"
"github.com/golang-jwt/jwt/v4"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
Expand All @@ -27,6 +25,7 @@ import (
"github.com/spf13/pflag"

"github.com/shipwright-io/build/pkg/bundle"
"github.com/shipwright-io/build/pkg/image"
)

type settings struct {
Expand Down Expand Up @@ -78,7 +77,7 @@ func Do(ctx context.Context) error {
return err
}

auth, err := resolveAuthBasedOnTarget(ref)
options, auth, err := image.GetOptions(ctx, ref, true, flagValues.secretPath, "Shipwright Build")
if err != nil {
return err
}
Expand All @@ -87,8 +86,7 @@ func Do(ctx context.Context) error {
img, err := bundle.PullAndUnpack(
ref,
flagValues.target,
remote.WithContext(ctx),
remote.WithAuth(auth))
options...)
if err != nil {
return err
}
Expand Down Expand Up @@ -116,87 +114,14 @@ func Do(ctx context.Context) error {
}

log.Printf("Deleting image %q", ref)
if err := Prune(ctx, ref, auth); err != nil {
if err := Prune(ref, options, *auth); err != nil {
return err
}
}

return nil
}

func resolveAuthBasedOnTarget(ref name.Reference) (authn.Authenticator, error) {
// In case no secret is mounted, use anonymous
if flagValues.secretPath == "" {
log.Printf("No access credentials provided, using anonymous mode")
return authn.Anonymous, nil
}

// Read the registry credentials from the well-known location if it exists
var mountedSecretDefaultFileName = filepath.Join(flagValues.secretPath, ".dockerconfigjson")
if _, err := os.Stat(mountedSecretDefaultFileName); err == nil {
return ResolveAuthBasedOnTargetUsingConfigFile(ref, mountedSecretDefaultFileName)
}

// Otherwise, treat secret path as a file (for none Kubernetes setups)
return ResolveAuthBasedOnTargetUsingConfigFile(ref, flagValues.secretPath)
}

// ResolveAuthBasedOnTargetUsingConfigFile resolves if possible the respective authenticator to be used for
// given image reference (full registry and image name)
func ResolveAuthBasedOnTargetUsingConfigFile(ref name.Reference, dockerConfigFile string) (authn.Authenticator, error) {
file, err := os.Open(dockerConfigFile)
if err != nil {
return nil, err
}
defer file.Close()

cf, err := config.LoadFromReader(file)
if err != nil {
return nil, err
}

// Look-up the respective registry server inside the credentials
registryName := ref.Context().RegistryStr()
if registryName == name.DefaultRegistry {
registryName = authn.DefaultAuthKey
}

authConfig, err := cf.GetAuthConfig(registryName)
if err != nil {
return nil, err
}

// Return an error in case the credentials do not match the desired
// registry and list all servers that actually are available
if authConfig.ServerAddress != registryName {
var servers []string
for name := range cf.GetAuthConfigs() {
servers = append(servers, name)
}

var availableConfigs string
if len(servers) > 0 {
availableConfigs = strings.Join(servers, ", ")
} else {
availableConfigs = "none"
}

return nil, fmt.Errorf("failed to find registry credentials for %s, available configurations: %s",
registryName,
availableConfigs,
)
}

log.Printf("Using provided access credentials for %s", registryName)
return authn.FromConfig(authn.AuthConfig{
Username: authConfig.Username,
Password: authConfig.Password,
Auth: authConfig.Auth,
IdentityToken: authConfig.IdentityToken,
RegistryToken: authConfig.RegistryToken,
}), nil
}

// Prune removes the image from the container registry
//
// Deleting a tag, or a whole repo is not as straightforward as initially
Expand All @@ -208,10 +133,10 @@ func ResolveAuthBasedOnTargetUsingConfigFile(ref name.Reference, dockerConfigFil
// is a provider switch to deal with images on DockerHub differently.
//
// DockerHub images:
// - In case the repository only has one tag, the repository is deleted.
// - If there are multiple tags, the tag to be deleted is overwritten
// with an empty image (to remove the content, and save quota).
// - Edge case would be no tags in the repository, which is ignored.
// - In case the repository only has one tag, the repository is deleted.
// - If there are multiple tags, the tag to be deleted is overwritten
// with an empty image (to remove the content, and save quota).
// - Edge case would be no tags in the repository, which is ignored.
//
// IBM Container Registry images:
// Custom delete API call has to be used, since ICR does not support the
Expand All @@ -223,11 +148,10 @@ func ResolveAuthBasedOnTargetUsingConfigFile(ref name.Reference, dockerConfigFil
//
// Other registries:
// Use standard spec delete API request to delete the provided tag.
//
func Prune(ctx context.Context, ref name.Reference, auth authn.Authenticator) error {
func Prune(ref name.Reference, options []remote.Option, auth authn.AuthConfig) error {
switch {
case strings.Contains(ref.Context().RegistryStr(), "docker.io"):
list, err := remote.List(ref.Context(), remote.WithContext(ctx), remote.WithAuth(auth))
list, err := remote.List(ref.Context(), options...)
if err != nil {
return err
}
Expand All @@ -237,14 +161,8 @@ func Prune(ctx context.Context, ref name.Reference, auth authn.Authenticator) er
return nil

case 1:
var authr *authn.AuthConfig
authr, err = auth.Authorization()
if err != nil {
return err
}

var token string
token, err = dockerHubLogin(authr.Username, authr.Password)
token, err = dockerHubLogin(auth.Username, auth.Password)
if err != nil {
return err
}
Expand All @@ -268,18 +186,12 @@ func Prune(ctx context.Context, ref name.Reference, auth authn.Authenticator) er
return remote.Write(
ref,
empty.Image,
remote.WithContext(ctx),
remote.WithAuth(auth),
options...,
)
}

case strings.Contains(ref.Context().RegistryStr(), "icr.io"):
authr, err := auth.Authorization()
if err != nil {
return err
}

token, accountID, err := icrLogin(ref.Context().RegistryStr(), authr.Username, authr.Password)
token, accountID, err := icrLogin(ref.Context().RegistryStr(), auth.Username, auth.Password)
if err != nil {
return err
}
Expand All @@ -289,8 +201,7 @@ func Prune(ctx context.Context, ref name.Reference, auth authn.Authenticator) er
default:
return remote.Delete(
ref,
remote.WithContext(ctx),
remote.WithAuth(auth),
options...,
)
}
}
Expand Down
17 changes: 9 additions & 8 deletions cmd/bundle/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
. "github.com/onsi/gomega"

. "github.com/shipwright-io/build/cmd/bundle"
"github.com/shipwright-io/build/pkg/image"

"github.com/google/go-containerregistry/pkg/name"
containerreg "github.com/google/go-containerregistry/pkg/v1"
Expand Down Expand Up @@ -144,19 +145,19 @@ var _ = Describe("Bundle Loader", func() {
var dockerConfigFile string

var copyImage = func(src, dst name.Reference) {
srcAuth, err := ResolveAuthBasedOnTargetUsingConfigFile(src, dockerConfigFile)
options, _, err := image.GetOptions(context.TODO(), src, true, dockerConfigFile, "test-agent")
Expect(err).ToNot(HaveOccurred())

srcDesc, err := remote.Get(src, remote.WithAuth(srcAuth))
srcDesc, err := remote.Get(src, options...)
Expect(err).ToNot(HaveOccurred())

srcImage, err := srcDesc.Image()
Expect(err).ToNot(HaveOccurred())

dstAuth, err := ResolveAuthBasedOnTargetUsingConfigFile(dst, dockerConfigFile)
options, _, err = image.GetOptions(context.TODO(), dst, true, dockerConfigFile, "test-agent")
Expect(err).ToNot(HaveOccurred())

err = remote.Write(dst, srcImage, remote.WithAuth(dstAuth))
err = remote.Write(dst, srcImage, options...)
Expect(err).ToNot(HaveOccurred())
}

Expand Down Expand Up @@ -190,11 +191,11 @@ var _ = Describe("Bundle Loader", func() {
ref, err := name.ParseReference(testImage)
Expect(err).ToNot(HaveOccurred())

auth, err := ResolveAuthBasedOnTargetUsingConfigFile(ref, dockerConfigFile)
options, auth, err := image.GetOptions(context.TODO(), ref, true, dockerConfigFile, "test-agent")
Expect(err).ToNot(HaveOccurred())

// Delete test image (best effort)
_ = Prune(context.TODO(), ref, auth)
_ = Prune(ref, options, *auth)
})

It("should pull and unpack an image from a private registry", func() {
Expand Down Expand Up @@ -223,10 +224,10 @@ var _ = Describe("Bundle Loader", func() {
ref, err := name.ParseReference(testImage)
Expect(err).ToNot(HaveOccurred())

auth, err := ResolveAuthBasedOnTargetUsingConfigFile(ref, dockerConfigFile)
options, _, err := image.GetOptions(context.TODO(), ref, true, dockerConfigFile, "test-agent")
Expect(err).ToNot(HaveOccurred())

_, err = remote.Head(ref, remote.WithAuth(auth))
_, err = remote.Head(ref, options...)
Expect(err).To(HaveOccurred())
})
})
Expand Down
13 changes: 8 additions & 5 deletions cmd/mutate-image/README.md → cmd/image-processing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ Copyright The Shipwright Contributors
SPDX-License-Identifier: Apache-2.0
-->
# Mutate Image Wrapper
# Image processing

As part of the build, the output image needs to be mutated (annotated and labeled). This package contains Shipwright Build owned image mutate code, which is using the [crane](https://github.com/google/go-containerregistry/tree/main/cmd/crane) in the background.
As part of the build, the output image needs to be mutated (annotated and labeled), and pushed. This package contains Shipwright Build owned image processing code.

## Features

- Mutate the image with [annotations](https://github.com/opencontainers/image-spec/blob/main/annotations.md)
- Mutate the image with labels
- Push the image

## Development

Expand All @@ -19,10 +20,12 @@ As part of the build, the output image needs to be mutated (annotated and labele
- Run it locally:

```sh
go run cmd/mutate-image/main.go \
go run cmd/image-processing/main.go \
--image $IMAGE \
--annotation "org.opencontainers.image.url=https://my-company.com/images" \
--label "[email protected]"
--label "[email protected]" \
[--insecure] \
[--push some-local-dir-or-tarball]
```

If we are trying to mutate the image in a private registry, authentication to the registry should be done before running the command.
Expand All @@ -34,7 +37,7 @@ As part of the build, the output image needs to be mutated (annotated and labele
--rm \
--volume $HOME/.docker/config.json:/.docker/config.json \
-e DOCKER_CONFIG=.docker \
$(KO_DOCKER_REPO=ko.local ko publish --bare ./cmd/mutate-image) \
$(KO_DOCKER_REPO=ko.local ko publish --bare ./cmd/image-processing) \
--image $IMAGE \
--annotation "org.opencontainers.image.url=https://my-company.com/images" \
--label "[email protected]"
Expand Down
Loading

0 comments on commit 815218b

Please sign in to comment.