mirror of
https://github.com/containers/skopeo.git
synced 2026-02-02 07:19:35 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85ce427969 | ||
|
|
c8ef2dcce3 | ||
|
|
b5b0a9cd81 | ||
|
|
e0171abca9 | ||
|
|
4773bf1895 | ||
|
|
7602ac68f8 | ||
|
|
7f996f3bdb | ||
|
|
78dc389125 | ||
|
|
34ed1100de | ||
|
|
051898442c | ||
|
|
89cd9b89b6 | ||
|
|
df2b9aedc8 | ||
|
|
6f884cd817 | ||
|
|
7e11ab4ada | ||
|
|
9b087c653c | ||
|
|
d79588e6c1 | ||
|
|
dc1e14f7a7 | ||
|
|
8191ef3ea1 | ||
|
|
902506dd73 | ||
|
|
3f98753bfd | ||
|
|
b2884205e7 | ||
|
|
fb1ade6d9e | ||
|
|
0d212fc3b5 | ||
|
|
40dd6507df |
@@ -26,7 +26,7 @@ env:
|
||||
FEDORA_NAME: "fedora-37"
|
||||
|
||||
# Google-cloud VM Images
|
||||
IMAGE_SUFFIX: "c20230405t152256z-f37f36d12"
|
||||
IMAGE_SUFFIX: "c6300530360713216"
|
||||
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
|
||||
|
||||
# Container FQIN's
|
||||
@@ -52,9 +52,7 @@ validate_task:
|
||||
image: '${SKOPEO_CIDEV_CONTAINER_FQIN}'
|
||||
cpu: 4
|
||||
memory: 8
|
||||
setup_script: |
|
||||
make tools
|
||||
test_script: |
|
||||
script: |
|
||||
make validate-local
|
||||
make vendor && hack/tree_status.sh
|
||||
|
||||
@@ -93,7 +91,7 @@ osx_task:
|
||||
export PATH=$GOPATH/bin:$PATH
|
||||
brew update
|
||||
brew install gpgme go go-md2man
|
||||
make tools
|
||||
go install golang.org/x/lint/golint@latest
|
||||
test_script: |
|
||||
export PATH=$GOPATH/bin:$PATH
|
||||
go version
|
||||
|
||||
10
.github/dependabot.yml
vendored
Normal file
10
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gomod
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "10:00"
|
||||
timezone: Europe/Berlin
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
15
.github/renovate.json5
vendored
15
.github/renovate.json5
vendored
@@ -56,4 +56,19 @@
|
||||
/*************************************************
|
||||
***** Golang-specific configuration options *****
|
||||
*************************************************/
|
||||
|
||||
"golang": {
|
||||
// N/B: LAST MATCHING RULE WINS
|
||||
// https://docs.renovatebot.com/configuration-options/#packagerules
|
||||
"packageRules": [
|
||||
// Package version retraction (https://go.dev/ref/mod#go-mod-file-retract)
|
||||
// is broken in Renovate
|
||||
// ref: https://github.com/renovatebot/renovate/issues/13012
|
||||
{
|
||||
"matchPackageNames": ["github.com/containers/common"],
|
||||
// Both v1.0.0 and v1.0.1 should be ignored.
|
||||
"allowedVersions": "!/v((1.0.0)|(1.0.1))$/"
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
pull-requests: write # for actions/stale to close stale PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v8
|
||||
- uses: actions/stale@v7
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'A friendly reminder that this issue had no activity for 30 days.'
|
||||
|
||||
26
.packit.sh
26
.packit.sh
@@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script handles any custom processing of the spec file generated using the `post-upstream-clone`
|
||||
# action and gets used by the fix-spec-file action in .packit.yaml.
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# Get Version from HEAD
|
||||
VERSION=$(grep '^const Version' version/version.go | cut -d\" -f2 | sed -e 's/-/~/')
|
||||
|
||||
# Generate source tarball
|
||||
git archive --prefix=skopeo-$VERSION/ -o skopeo-$VERSION.tar.gz HEAD
|
||||
|
||||
# RPM Spec modifications
|
||||
|
||||
# Update Version in spec with Version from Cargo.toml
|
||||
sed -i "s/^Version:.*/Version: $VERSION/" skopeo.spec
|
||||
|
||||
# Update Release in spec with Packit's release envvar
|
||||
sed -i "s/^Release:.*/Release: $PACKIT_RPMSPEC_RELEASE%{?dist}/" skopeo.spec
|
||||
|
||||
# Update Source tarball name in spec
|
||||
sed -i "s/^Source:.*.tar.gz/Source: skopeo-$VERSION.tar.gz/" skopeo.spec
|
||||
|
||||
# Update setup macro to use the correct build dir
|
||||
sed -i "s/^%setup.*/%autosetup -Sgit -n %{name}-$VERSION/" skopeo.spec
|
||||
34
.packit.yaml
34
.packit.yaml
@@ -1,34 +0,0 @@
|
||||
---
|
||||
# See the documentation for more information:
|
||||
# https://packit.dev/docs/configuration/
|
||||
|
||||
# NOTE: The Packit copr_build tasks help to check if every commit builds on
|
||||
# supported Fedora and CentOS Stream arches.
|
||||
# They do not block the current Cirrus-based workflow.
|
||||
|
||||
# Build targets can be found at:
|
||||
# https://copr.fedorainfracloud.org/coprs/rhcontainerbot/packit-builds/
|
||||
|
||||
specfile_path: skopeo.spec
|
||||
|
||||
jobs:
|
||||
- &copr
|
||||
job: copr_build
|
||||
trigger: pull_request
|
||||
owner: rhcontainerbot
|
||||
project: packit-builds
|
||||
enable_net: true
|
||||
srpm_build_deps:
|
||||
- make
|
||||
- rpkg
|
||||
actions:
|
||||
post-upstream-clone:
|
||||
- "rpkg spec --outdir ./"
|
||||
fix-spec-file:
|
||||
- "bash .packit.sh"
|
||||
|
||||
- <<: *copr
|
||||
# Run on commit to main branch
|
||||
trigger: commit
|
||||
branch: main
|
||||
project: podman-next
|
||||
49
Makefile
49
Makefile
@@ -38,6 +38,13 @@ endif
|
||||
export CONTAINER_RUNTIME ?= $(if $(shell command -v podman ;),podman,docker)
|
||||
GOMD2MAN ?= $(if $(shell command -v go-md2man ;),go-md2man,$(GOBIN)/go-md2man)
|
||||
|
||||
# Go module support: set `-mod=vendor` to use the vendored sources.
|
||||
# See also hack/make.sh.
|
||||
ifeq ($(shell go help mod >/dev/null 2>&1 && echo true), true)
|
||||
GO:=GO111MODULE=on $(GO)
|
||||
MOD_VENDOR=-mod=vendor
|
||||
endif
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
override GOGCFLAGS += -N -l
|
||||
endif
|
||||
@@ -49,11 +56,17 @@ ifeq ($(GOOS), linux)
|
||||
endif
|
||||
|
||||
# If $TESTFLAGS is set, it is passed as extra arguments to 'go test'.
|
||||
# You can select certain tests to run, with `-run <regex>` for example:
|
||||
# You can increase test output verbosity with the option '-test.vv'.
|
||||
# You can select certain tests to run, with `-test.run <regex>` for example:
|
||||
#
|
||||
# make test-unit TESTFLAGS='-run ^TestManifestDigest$'
|
||||
# make test-integration TESTFLAGS='-run copySuite.TestCopy.*'
|
||||
export TESTFLAGS ?= -timeout=15m
|
||||
# make test-unit TESTFLAGS='-test.run ^TestManifestDigest$'
|
||||
#
|
||||
# For integration test, we use [gocheck](https://labix.org/gocheck).
|
||||
# You can increase test output verbosity with the option '-check.vv'.
|
||||
# You can limit test selection with `-check.f <regex>`, for example:
|
||||
#
|
||||
# make test-integration TESTFLAGS='-check.f CopySuite.TestCopy.*'
|
||||
export TESTFLAGS ?= -v -check.v -test.timeout=15m
|
||||
|
||||
# This is assumed to be set non-empty when operating inside a CI/automation environment
|
||||
CI ?=
|
||||
@@ -126,9 +139,9 @@ binary: cmd/skopeo
|
||||
# Build w/o using containers
|
||||
.PHONY: bin/skopeo
|
||||
bin/skopeo:
|
||||
$(GO) build ${GO_DYN_FLAGS} ${SKOPEO_LDFLAGS} -gcflags "$(GOGCFLAGS)" -tags "$(BUILDTAGS)" -o $@ ./cmd/skopeo
|
||||
$(GO) build $(MOD_VENDOR) ${GO_DYN_FLAGS} ${SKOPEO_LDFLAGS} -gcflags "$(GOGCFLAGS)" -tags "$(BUILDTAGS)" -o $@ ./cmd/skopeo
|
||||
bin/skopeo.%:
|
||||
GOOS=$(word 2,$(subst ., ,$@)) GOARCH=$(word 3,$(subst ., ,$@)) $(GO) build ${SKOPEO_LDFLAGS} -tags "containers_image_openpgp $(BUILDTAGS)" -o $@ ./cmd/skopeo
|
||||
GOOS=$(word 2,$(subst ., ,$@)) GOARCH=$(word 3,$(subst ., ,$@)) $(GO) build $(MOD_VENDOR) ${SKOPEO_LDFLAGS} -tags "containers_image_openpgp $(BUILDTAGS)" -o $@ ./cmd/skopeo
|
||||
local-cross: bin/skopeo.darwin.amd64 bin/skopeo.linux.arm bin/skopeo.linux.arm64 bin/skopeo.windows.386.exe bin/skopeo.windows.amd64.exe
|
||||
|
||||
$(MANPAGES): %: %.md
|
||||
@@ -181,11 +194,6 @@ install-completions: completions
|
||||
shell:
|
||||
$(CONTAINER_RUN) bash
|
||||
|
||||
tools:
|
||||
if [ ! -x "$(GOBIN)/golangci-lint" ]; then \
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) v1.52.2 ; \
|
||||
fi
|
||||
|
||||
check: validate test-unit test-integration test-system
|
||||
|
||||
test-integration:
|
||||
@@ -198,8 +206,7 @@ test-integration:
|
||||
|
||||
# Intended for CI, assumed to be running in quay.io/libpod/skopeo_cidev container.
|
||||
test-integration-local: bin/skopeo
|
||||
hack/warn-destructive-tests.sh
|
||||
hack/test-integration.sh
|
||||
hack/make.sh test-integration
|
||||
|
||||
# complicated set of options needed to run podman-in-podman
|
||||
test-system:
|
||||
@@ -215,8 +222,7 @@ test-system:
|
||||
|
||||
# Intended for CI, assumed to already be running in quay.io/libpod/skopeo_cidev container.
|
||||
test-system-local: bin/skopeo
|
||||
hack/warn-destructive-tests.sh
|
||||
hack/test-system.sh
|
||||
hack/make.sh test-system
|
||||
|
||||
test-unit:
|
||||
# Just call (make test unit-local) here instead of worrying about environment differences
|
||||
@@ -230,22 +236,19 @@ test-all-local: validate-local validate-docs test-unit-local
|
||||
|
||||
.PHONY: validate-local
|
||||
validate-local:
|
||||
hack/validate-git-marks.sh
|
||||
hack/validate-gofmt.sh
|
||||
GOBIN=$(GOBIN) hack/validate-lint.sh
|
||||
BUILDTAGS="${BUILDTAGS}" hack/validate-vet.sh
|
||||
BUILDTAGS="${BUILDTAGS}" hack/make.sh validate-git-marks validate-gofmt validate-lint validate-vet
|
||||
|
||||
# This invokes bin/skopeo, hence cannot be run as part of validate-local
|
||||
.PHONY: validate-docs
|
||||
validate-docs: bin/skopeo
|
||||
validate-docs:
|
||||
hack/man-page-checker
|
||||
hack/xref-helpmsgs-manpages
|
||||
|
||||
test-unit-local:
|
||||
$(GO) test -tags "$(BUILDTAGS)" $$($(GO) list -tags "$(BUILDTAGS)" -e ./... | grep -v '^github\.com/containers/skopeo/\(integration\|vendor/.*\)$$')
|
||||
test-unit-local: bin/skopeo
|
||||
$(GO) test $(MOD_VENDOR) -tags "$(BUILDTAGS)" $$($(GO) list $(MOD_VENDOR) -tags "$(BUILDTAGS)" -e ./... | grep -v '^github\.com/containers/skopeo/\(integration\|vendor/.*\)$$')
|
||||
|
||||
vendor:
|
||||
$(GO) mod tidy
|
||||
$(GO) mod tidy -compat=1.17
|
||||
$(GO) mod vendor
|
||||
$(GO) mod verify
|
||||
|
||||
|
||||
18
README.md
18
README.md
@@ -1,4 +1,8 @@
|
||||
<img src="https://cdn.rawgit.com/containers/skopeo/main/docs/skopeo.svg" width="250" alt="Skopeo">
|
||||
<!--- skopeo [](https://travis-ci.org/containers/skopeo)
|
||||
=
|
||||
--->
|
||||
|
||||
<img src="https://cdn.rawgit.com/containers/skopeo/main/docs/skopeo.svg" width="250">
|
||||
|
||||
----
|
||||
|
||||
@@ -39,12 +43,6 @@ Skopeo works with API V2 container image registries such as [docker.io](https://
|
||||
* oci:path:tag
|
||||
An image tag in a directory compliant with "Open Container Image Layout Specification" at path.
|
||||
|
||||
[Obtaining skopeo](./install.md)
|
||||
-
|
||||
|
||||
For a detailed description how to install or build skopeo, see
|
||||
[install.md](./install.md).
|
||||
|
||||
## Inspecting a repository
|
||||
`skopeo` is able to _inspect_ a repository on a container registry and fetch images layers.
|
||||
The _inspect_ command fetches the repository's manifest and it is able to show you a `docker inspect`-like
|
||||
@@ -195,6 +193,12 @@ $ skopeo inspect --creds=testuser:testpassword docker://myregistrydomain.com:500
|
||||
$ skopeo copy --src-creds=testuser:testpassword docker://myregistrydomain.com:5000/private oci:local_oci_image
|
||||
```
|
||||
|
||||
[Obtaining skopeo](./install.md)
|
||||
-
|
||||
|
||||
For a detailed description how to install or build skopeo, see
|
||||
[install.md](./install.md).
|
||||
|
||||
Contributing
|
||||
-
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ func TestGenerateSigstoreKey(t *testing.T) {
|
||||
// we have to trigger a write failure.
|
||||
|
||||
// Success
|
||||
// Just a smoke-test, usability of the keys is tested in the generate implementation.
|
||||
// Just a smoke-test, useability of the keys is tested in the generate implementation.
|
||||
dir := t.TempDir()
|
||||
prefix := filepath.Join(dir, "prefix")
|
||||
passphraseFile := filepath.Join(dir, "passphrase")
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
|
||||
"github.com/containers/common/pkg/report"
|
||||
"github.com/containers/common/pkg/retry"
|
||||
@@ -51,8 +53,8 @@ See skopeo(1) section "IMAGE NAMES" for the expected format
|
||||
`, strings.Join(transports.ListNames(), ", ")),
|
||||
RunE: commandAction(opts.run),
|
||||
Example: `skopeo inspect docker://registry.fedoraproject.org/fedora
|
||||
skopeo inspect --config docker://docker.io/alpine
|
||||
skopeo inspect --format "Name: {{.Name}} Digest: {{.Digest}}" docker://registry.access.redhat.com/ubi8`,
|
||||
skopeo inspect --config docker://docker.io/alpine
|
||||
skopeo inspect --format "Name: {{.Name}} Digest: {{.Digest}}" docker://registry.access.redhat.com/ubi8`,
|
||||
ValidArgsFunction: autocompleteSupportedTransports,
|
||||
}
|
||||
adjustUsage(cmd)
|
||||
@@ -72,6 +74,7 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
|
||||
rawManifest []byte
|
||||
src types.ImageSource
|
||||
imgInspect *types.ImageInspectInfo
|
||||
data []interface{}
|
||||
)
|
||||
ctx, cancel := opts.global.commandTimeoutContext()
|
||||
defer cancel()
|
||||
@@ -148,7 +151,18 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
|
||||
}, opts.retryOpts); err != nil {
|
||||
return fmt.Errorf("Error reading OCI-formatted configuration data: %w", err)
|
||||
}
|
||||
if err := opts.writeOutput(stdout, config); err != nil {
|
||||
if report.IsJSON(opts.format) || opts.format == "" {
|
||||
var out []byte
|
||||
out, err = json.MarshalIndent(config, "", " ")
|
||||
if err == nil {
|
||||
fmt.Fprintf(stdout, "%s\n", string(out))
|
||||
}
|
||||
} else {
|
||||
row := "{{range . }}" + report.NormalizeFormat(opts.format) + "{{end}}"
|
||||
data = append(data, config)
|
||||
err = printTmpl(stdout, row, data)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error writing OCI-formatted configuration data to standard output: %w", err)
|
||||
}
|
||||
return nil
|
||||
@@ -214,23 +228,23 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
|
||||
logrus.Warnf("Registry disallows tag list retrieval; skipping")
|
||||
}
|
||||
}
|
||||
return opts.writeOutput(stdout, outputData)
|
||||
}
|
||||
|
||||
// writeOutput writes data depending on opts.format to stdout
|
||||
func (opts *inspectOptions) writeOutput(stdout io.Writer, data any) error {
|
||||
if report.IsJSON(opts.format) || opts.format == "" {
|
||||
out, err := json.MarshalIndent(data, "", " ")
|
||||
out, err := json.MarshalIndent(outputData, "", " ")
|
||||
if err == nil {
|
||||
fmt.Fprintf(stdout, "%s\n", string(out))
|
||||
}
|
||||
return err
|
||||
}
|
||||
row := "{{range . }}" + report.NormalizeFormat(opts.format) + "{{end}}"
|
||||
data = append(data, outputData)
|
||||
return printTmpl(stdout, row, data)
|
||||
}
|
||||
|
||||
rpt, err := report.New(stdout, "skopeo inspect").Parse(report.OriginUser, opts.format)
|
||||
func printTmpl(stdout io.Writer, row string, data []interface{}) error {
|
||||
t, err := template.New("skopeo inspect").Parse(row)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rpt.Flush()
|
||||
return rpt.Execute([]any{data})
|
||||
w := tabwriter.NewWriter(stdout, 8, 2, 2, ' ', 0)
|
||||
return t.Execute(w, data)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// tagListOutput is the output format of (skopeo list-tags), primarily so that we can format it with a simple json.MarshalIndent.
|
||||
@@ -38,7 +37,10 @@ var transportHandlers = map[string]func(ctx context.Context, sys *types.SystemCo
|
||||
|
||||
// supportedTransports returns all the supported transports
|
||||
func supportedTransports(joinStr string) string {
|
||||
res := maps.Keys(transportHandlers)
|
||||
res := make([]string, 0, len(transportHandlers))
|
||||
for handlerName := range transportHandlers {
|
||||
res = append(res, handlerName)
|
||||
}
|
||||
sort.Strings(res)
|
||||
return strings.Join(res, joinStr)
|
||||
}
|
||||
@@ -82,12 +84,12 @@ func parseDockerRepositoryReference(refString string) (types.ImageReference, err
|
||||
return nil, fmt.Errorf("docker: image reference %s does not start with %s://", refString, docker.Transport.Name())
|
||||
}
|
||||
|
||||
_, dockerImageName, hasColon := strings.Cut(refString, ":")
|
||||
if !hasColon {
|
||||
parts := strings.SplitN(refString, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf(`Invalid image name "%s", expected colon-separated transport:reference`, refString)
|
||||
}
|
||||
|
||||
ref, err := reference.ParseNormalizedNamed(strings.TrimPrefix(dockerImageName, "//"))
|
||||
ref, err := reference.ParseNormalizedNamed(strings.TrimPrefix(parts[1], "//"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -128,7 +130,7 @@ func listDockerRepoTags(ctx context.Context, sys *types.SystemContext, opts *tag
|
||||
}
|
||||
|
||||
// return the tagLists from a docker archive file
|
||||
func listDockerArchiveTags(_ context.Context, sys *types.SystemContext, _ *tagsOptions, userInput string) (repositoryName string, tagListing []string, err error) {
|
||||
func listDockerArchiveTags(ctx context.Context, sys *types.SystemContext, opts *tagsOptions, userInput string) (repositoryName string, tagListing []string, err error) {
|
||||
ref, err := alltransports.ParseImageName(userInput)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -16,6 +16,7 @@ func TestDockerRepositoryReferenceParser(t *testing.T) {
|
||||
{"docker://somehost.com"}, // Valid default expansion
|
||||
{"docker://nginx"}, // Valid default expansion
|
||||
} {
|
||||
|
||||
ref, err := parseDockerRepositoryReference(test[0])
|
||||
require.NoError(t, err)
|
||||
expected, err := alltransports.ParseImageName(test[0])
|
||||
@@ -46,6 +47,7 @@ func TestDockerRepositoryReferenceParserDrift(t *testing.T) {
|
||||
{"docker://somehost.com", "docker.io/library/somehost.com"}, // Valid default expansion
|
||||
{"docker://nginx", "docker.io/library/nginx"}, // Valid default expansion
|
||||
} {
|
||||
|
||||
ref, err := parseDockerRepositoryReference(test[0])
|
||||
ref2, err2 := alltransports.ParseImageName(test[0])
|
||||
|
||||
|
||||
@@ -55,12 +55,14 @@ func createApp() (*cobra.Command, *globalOptions) {
|
||||
opts := globalOptions{}
|
||||
|
||||
rootCommand := &cobra.Command{
|
||||
Use: "skopeo",
|
||||
Long: "Various operations with container images and container image registries",
|
||||
RunE: requireSubcommand,
|
||||
PersistentPreRunE: opts.before,
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
Use: "skopeo",
|
||||
Long: "Various operations with container images and container image registries",
|
||||
RunE: requireSubcommand,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return opts.before(cmd)
|
||||
},
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
// Hide the completion command which is provided by cobra
|
||||
CompletionOptions: cobra.CompletionOptions{HiddenDefaultCmd: true},
|
||||
// This is documented to parse "local" (non-PersistentFlags) flags of parent commands before
|
||||
@@ -113,7 +115,7 @@ func createApp() (*cobra.Command, *globalOptions) {
|
||||
}
|
||||
|
||||
// before is run by the cli package for any command, before running the command-specific handler.
|
||||
func (opts *globalOptions) before(cmd *cobra.Command, args []string) error {
|
||||
func (opts *globalOptions) before(cmd *cobra.Command) error {
|
||||
if opts.debug {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ type request struct {
|
||||
// Method is the name of the function
|
||||
Method string `json:"method"`
|
||||
// Args is the arguments (parsed inside the function)
|
||||
Args []any `json:"args"`
|
||||
Args []interface{} `json:"args"`
|
||||
}
|
||||
|
||||
// reply is serialized to JSON as the return value from a function call.
|
||||
@@ -123,7 +123,7 @@ type reply struct {
|
||||
// Success is true if and only if the call succeeded.
|
||||
Success bool `json:"success"`
|
||||
// Value is an arbitrary value (or values, as array/map) returned from the call.
|
||||
Value any `json:"value"`
|
||||
Value interface{} `json:"value"`
|
||||
// PipeID is an index into open pipes, and should be passed to FinishPipe
|
||||
PipeID uint32 `json:"pipeid"`
|
||||
// Error should be non-empty if Success == false
|
||||
@@ -133,7 +133,7 @@ type reply struct {
|
||||
// replyBuf is our internal deserialization of reply plus optional fd
|
||||
type replyBuf struct {
|
||||
// value will be converted to a reply Value
|
||||
value any
|
||||
value interface{}
|
||||
// fd is the read half of a pipe, passed back to the client
|
||||
fd *os.File
|
||||
// pipeid will be provided to the client as PipeID, an index into our open pipes
|
||||
@@ -185,7 +185,7 @@ type convertedLayerInfo struct {
|
||||
}
|
||||
|
||||
// Initialize performs one-time initialization, and returns the protocol version
|
||||
func (h *proxyHandler) Initialize(args []any) (replyBuf, error) {
|
||||
func (h *proxyHandler) Initialize(args []interface{}) (replyBuf, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
@@ -214,7 +214,7 @@ func (h *proxyHandler) Initialize(args []any) (replyBuf, error) {
|
||||
|
||||
// OpenImage accepts a string image reference i.e. TRANSPORT:REF - like `skopeo copy`.
|
||||
// The return value is an opaque integer handle.
|
||||
func (h *proxyHandler) OpenImage(args []any) (replyBuf, error) {
|
||||
func (h *proxyHandler) OpenImage(args []interface{}) (replyBuf, error) {
|
||||
return h.openImageImpl(args, false)
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ func isNotFoundImageError(err error) bool {
|
||||
errors.Is(err, ocilayout.ImageNotFoundError{})
|
||||
}
|
||||
|
||||
func (h *proxyHandler) openImageImpl(args []any, allowNotFound bool) (replyBuf, error) {
|
||||
func (h *proxyHandler) openImageImpl(args []interface{}, allowNotFound bool) (replyBuf, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
var ret replyBuf
|
||||
@@ -282,11 +282,11 @@ func (h *proxyHandler) openImageImpl(args []any, allowNotFound bool) (replyBuf,
|
||||
// OpenImage accepts a string image reference i.e. TRANSPORT:REF - like `skopeo copy`.
|
||||
// The return value is an opaque integer handle. If the image does not exist, zero
|
||||
// is returned.
|
||||
func (h *proxyHandler) OpenImageOptional(args []any) (replyBuf, error) {
|
||||
func (h *proxyHandler) OpenImageOptional(args []interface{}) (replyBuf, error) {
|
||||
return h.openImageImpl(args, true)
|
||||
}
|
||||
|
||||
func (h *proxyHandler) CloseImage(args []any) (replyBuf, error) {
|
||||
func (h *proxyHandler) CloseImage(args []interface{}) (replyBuf, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
var ret replyBuf
|
||||
@@ -307,7 +307,7 @@ func (h *proxyHandler) CloseImage(args []any) (replyBuf, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func parseImageID(v any) (uint32, error) {
|
||||
func parseImageID(v interface{}) (uint32, error) {
|
||||
imgidf, ok := v.(float64)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("expecting integer imageid, not %T", v)
|
||||
@@ -316,7 +316,7 @@ func parseImageID(v any) (uint32, error) {
|
||||
}
|
||||
|
||||
// parseUint64 validates that a number fits inside a JavaScript safe integer
|
||||
func parseUint64(v any) (uint64, error) {
|
||||
func parseUint64(v interface{}) (uint64, error) {
|
||||
f, ok := v.(float64)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("expecting numeric, not %T", v)
|
||||
@@ -327,7 +327,7 @@ func parseUint64(v any) (uint64, error) {
|
||||
return uint64(f), nil
|
||||
}
|
||||
|
||||
func (h *proxyHandler) parseImageFromID(v any) (*openImage, error) {
|
||||
func (h *proxyHandler) parseImageFromID(v interface{}) (*openImage, error) {
|
||||
imgid, err := parseImageID(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -357,7 +357,7 @@ func (h *proxyHandler) allocPipe() (*os.File, *activePipe, error) {
|
||||
|
||||
// returnBytes generates a return pipe() from a byte array
|
||||
// In the future it might be nicer to return this via memfd_create()
|
||||
func (h *proxyHandler) returnBytes(retval any, buf []byte) (replyBuf, error) {
|
||||
func (h *proxyHandler) returnBytes(retval interface{}, buf []byte) (replyBuf, error) {
|
||||
var ret replyBuf
|
||||
piper, f, err := h.allocPipe()
|
||||
if err != nil {
|
||||
@@ -419,7 +419,7 @@ func (h *proxyHandler) cacheTargetManifest(img *openImage) error {
|
||||
|
||||
// GetManifest returns a copy of the manifest, converted to OCI format, along with the original digest.
|
||||
// Manifest lists are resolved to the current operating system and architecture.
|
||||
func (h *proxyHandler) GetManifest(args []any) (replyBuf, error) {
|
||||
func (h *proxyHandler) GetManifest(args []interface{}) (replyBuf, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
@@ -490,7 +490,7 @@ func (h *proxyHandler) GetManifest(args []any) (replyBuf, error) {
|
||||
|
||||
// GetFullConfig returns a copy of the image configuration, converted to OCI format.
|
||||
// https://github.com/opencontainers/image-spec/blob/main/config.md
|
||||
func (h *proxyHandler) GetFullConfig(args []any) (replyBuf, error) {
|
||||
func (h *proxyHandler) GetFullConfig(args []interface{}) (replyBuf, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
@@ -527,7 +527,7 @@ func (h *proxyHandler) GetFullConfig(args []any) (replyBuf, error) {
|
||||
// GetConfig returns a copy of the container runtime configuration, converted to OCI format.
|
||||
// Note that due to a historical mistake, this returns not the full image configuration,
|
||||
// but just the container runtime configuration. You should use GetFullConfig instead.
|
||||
func (h *proxyHandler) GetConfig(args []any) (replyBuf, error) {
|
||||
func (h *proxyHandler) GetConfig(args []interface{}) (replyBuf, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
@@ -562,7 +562,7 @@ func (h *proxyHandler) GetConfig(args []any) (replyBuf, error) {
|
||||
}
|
||||
|
||||
// GetBlob fetches a blob, performing digest verification.
|
||||
func (h *proxyHandler) GetBlob(args []any) (replyBuf, error) {
|
||||
func (h *proxyHandler) GetBlob(args []interface{}) (replyBuf, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
@@ -632,7 +632,7 @@ func (h *proxyHandler) GetBlob(args []any) (replyBuf, error) {
|
||||
// This needs to be called since the data returned by GetManifest() does not allow to correctly
|
||||
// calling GetBlob() for the containers-storage: transport (which doesn’t store the original compressed
|
||||
// representations referenced in the manifest).
|
||||
func (h *proxyHandler) GetLayerInfo(args []any) (replyBuf, error) {
|
||||
func (h *proxyHandler) GetLayerInfo(args []interface{}) (replyBuf, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
@@ -668,7 +668,7 @@ func (h *proxyHandler) GetLayerInfo(args []any) (replyBuf, error) {
|
||||
layerInfos = img.LayerInfos()
|
||||
}
|
||||
|
||||
layers := make([]convertedLayerInfo, 0, len(layerInfos))
|
||||
var layers []convertedLayerInfo
|
||||
for _, layer := range layerInfos {
|
||||
layers = append(layers, convertedLayerInfo{layer.Digest, layer.Size, layer.MediaType})
|
||||
}
|
||||
@@ -678,7 +678,7 @@ func (h *proxyHandler) GetLayerInfo(args []any) (replyBuf, error) {
|
||||
}
|
||||
|
||||
// FinishPipe waits for the worker goroutine to finish, and closes the write side of the pipe.
|
||||
func (h *proxyHandler) FinishPipe(args []any) (replyBuf, error) {
|
||||
func (h *proxyHandler) FinishPipe(args []interface{}) (replyBuf, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/v5/pkg/cli"
|
||||
"github.com/containers/image/v5/signature"
|
||||
@@ -42,12 +41,12 @@ func (opts *standaloneSignOptions) run(args []string, stdout io.Writer) error {
|
||||
|
||||
manifest, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading %s: %w", manifestPath, err)
|
||||
return fmt.Errorf("Error reading %s: %v", manifestPath, err)
|
||||
}
|
||||
|
||||
mech, err := signature.NewGPGSigningMechanism()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error initializing GPG: %w", err)
|
||||
return fmt.Errorf("Error initializing GPG: %v", err)
|
||||
}
|
||||
defer mech.Close()
|
||||
|
||||
@@ -58,31 +57,25 @@ func (opts *standaloneSignOptions) run(args []string, stdout io.Writer) error {
|
||||
|
||||
signature, err := signature.SignDockerManifestWithOptions(manifest, dockerReference, mech, fingerprint, &signature.SignOptions{Passphrase: passphrase})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating signature: %w", err)
|
||||
return fmt.Errorf("Error creating signature: %v", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(opts.output, signature, 0644); err != nil {
|
||||
return fmt.Errorf("Error writing signature to %s: %w", opts.output, err)
|
||||
return fmt.Errorf("Error writing signature to %s: %v", opts.output, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type standaloneVerifyOptions struct {
|
||||
publicKeyFile string
|
||||
}
|
||||
|
||||
func standaloneVerifyCmd() *cobra.Command {
|
||||
opts := standaloneVerifyOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "standalone-verify MANIFEST DOCKER-REFERENCE KEY-FINGERPRINTS SIGNATURE",
|
||||
Use: "standalone-verify MANIFEST DOCKER-REFERENCE KEY-FINGERPRINT SIGNATURE",
|
||||
Short: "Verify a signature using local files",
|
||||
Long: `Verify a signature using local files
|
||||
|
||||
KEY-FINGERPRINTS can be a comma separated list of fingerprints, or "any" if you trust all the keys in the public key file.`,
|
||||
RunE: commandAction(opts.run),
|
||||
RunE: commandAction(opts.run),
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&opts.publicKeyFile, "public-key-file", "", `File containing public keys. If not specified, will use local GPG keys.`)
|
||||
adjustUsage(cmd)
|
||||
return cmd
|
||||
}
|
||||
@@ -93,51 +86,29 @@ func (opts *standaloneVerifyOptions) run(args []string, stdout io.Writer) error
|
||||
}
|
||||
manifestPath := args[0]
|
||||
expectedDockerReference := args[1]
|
||||
expectedFingerprints := strings.Split(args[2], ",")
|
||||
expectedFingerprint := args[2]
|
||||
signaturePath := args[3]
|
||||
|
||||
if opts.publicKeyFile == "" && len(expectedFingerprints) == 1 && expectedFingerprints[0] == "any" {
|
||||
return fmt.Errorf("Cannot use any fingerprint without a public key file")
|
||||
}
|
||||
unverifiedManifest, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading manifest from %s: %w", manifestPath, err)
|
||||
return fmt.Errorf("Error reading manifest from %s: %v", manifestPath, err)
|
||||
}
|
||||
unverifiedSignature, err := os.ReadFile(signaturePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading signature from %s: %w", signaturePath, err)
|
||||
return fmt.Errorf("Error reading signature from %s: %v", signaturePath, err)
|
||||
}
|
||||
|
||||
var mech signature.SigningMechanism
|
||||
var publicKeyfingerprints []string
|
||||
if opts.publicKeyFile != "" {
|
||||
publicKeys, err := os.ReadFile(opts.publicKeyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading public keys from %s: %w", opts.publicKeyFile, err)
|
||||
}
|
||||
mech, publicKeyfingerprints, err = signature.NewEphemeralGPGSigningMechanism(publicKeys)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error initializing GPG: %w", err)
|
||||
|
||||
}
|
||||
} else {
|
||||
mech, err = signature.NewGPGSigningMechanism()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error initializing GPG: %w", err)
|
||||
}
|
||||
mech, err := signature.NewGPGSigningMechanism()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error initializing GPG: %v", err)
|
||||
}
|
||||
defer mech.Close()
|
||||
|
||||
if len(expectedFingerprints) == 1 && expectedFingerprints[0] == "any" {
|
||||
expectedFingerprints = publicKeyfingerprints
|
||||
}
|
||||
|
||||
sig, verificationFingerprint, err := signature.VerifyImageManifestSignatureUsingKeyIdentityList(unverifiedSignature, unverifiedManifest, expectedDockerReference, mech, expectedFingerprints)
|
||||
sig, err := signature.VerifyDockerManifestSignature(unverifiedSignature, unverifiedManifest, expectedDockerReference, mech, expectedFingerprint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error verifying signature: %w", err)
|
||||
return fmt.Errorf("Error verifying signature: %v", err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(stdout, "Signature verified using fingerprint %s, digest %s\n", verificationFingerprint, sig.DockerManifestDigest)
|
||||
fmt.Fprintf(stdout, "Signature verified, digest %s\n", sig.DockerManifestDigest)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -170,7 +141,7 @@ func (opts *untrustedSignatureDumpOptions) run(args []string, stdout io.Writer)
|
||||
|
||||
untrustedSignature, err := os.ReadFile(untrustedSignaturePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading untrusted signature from %s: %w", untrustedSignaturePath, err)
|
||||
return fmt.Errorf("Error reading untrusted signature from %s: %v", untrustedSignaturePath, err)
|
||||
}
|
||||
|
||||
untrustedInfo, err := signature.GetUntrustedSignatureInformationWithoutVerifying(untrustedSignature)
|
||||
|
||||
@@ -127,36 +127,11 @@ func TestStandaloneVerify(t *testing.T) {
|
||||
dockerReference, fixturesTestKeyFingerprint, "fixtures/corrupt.signature")
|
||||
assertTestFailed(t, out, err, "Error verifying signature")
|
||||
|
||||
// Error using any without a public key file
|
||||
out, err = runSkopeo("standalone-verify", manifestPath,
|
||||
dockerReference, "any", signaturePath)
|
||||
assertTestFailed(t, out, err, "Cannot use any fingerprint without a public key file")
|
||||
|
||||
// Success
|
||||
out, err = runSkopeo("standalone-verify", manifestPath,
|
||||
dockerReference, fixturesTestKeyFingerprint, signaturePath)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Signature verified using fingerprint "+fixturesTestKeyFingerprint+", digest "+fixturesTestImageManifestDigest.String()+"\n", out)
|
||||
|
||||
// Using multiple fingerprints
|
||||
out, err = runSkopeo("standalone-verify", manifestPath,
|
||||
dockerReference, "0123456789ABCDEF0123456789ABCDEF01234567,"+fixturesTestKeyFingerprint+",DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", signaturePath)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Signature verified using fingerprint "+fixturesTestKeyFingerprint+", digest "+fixturesTestImageManifestDigest.String()+"\n", out)
|
||||
|
||||
// Using a public key file
|
||||
t.Setenv("GNUPGHOME", "")
|
||||
out, err = runSkopeo("standalone-verify", "--public-key-file", "fixtures/pubring.gpg", manifestPath,
|
||||
dockerReference, fixturesTestKeyFingerprint, signaturePath)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Signature verified using fingerprint "+fixturesTestKeyFingerprint+", digest "+fixturesTestImageManifestDigest.String()+"\n", out)
|
||||
|
||||
// Using a public key file matching any public key
|
||||
t.Setenv("GNUPGHOME", "")
|
||||
out, err = runSkopeo("standalone-verify", "--public-key-file", "fixtures/pubring.gpg", manifestPath,
|
||||
dockerReference, "any", signaturePath)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Signature verified using fingerprint "+fixturesTestKeyFingerprint+", digest "+fixturesTestImageManifestDigest.String()+"\n", out)
|
||||
assert.Equal(t, "Signature verified, digest "+fixturesTestImageManifestDigest.String()+"\n", out)
|
||||
}
|
||||
|
||||
func TestUntrustedSignatureDump(t *testing.T) {
|
||||
|
||||
@@ -26,8 +26,7 @@ import (
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/exp/slices"
|
||||
"gopkg.in/yaml.v3"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// syncOptions contains information retrieved from the skopeo sync command line.
|
||||
@@ -132,12 +131,12 @@ See skopeo-sync(1) for details.
|
||||
}
|
||||
|
||||
// UnmarshalYAML is the implementation of the Unmarshaler interface method
|
||||
// for the tlsVerifyConfig type.
|
||||
// method for the tlsVerifyConfig type.
|
||||
// It unmarshals the 'tls-verify' YAML key so that, when they key is not
|
||||
// specified, tls verification is enforced.
|
||||
func (tls *tlsVerifyConfig) UnmarshalYAML(value *yaml.Node) error {
|
||||
func (tls *tlsVerifyConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var verify bool
|
||||
if err := value.Decode(&verify); err != nil {
|
||||
if err := unmarshal(&verify); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -524,17 +523,26 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) (retErr error) {
|
||||
}()
|
||||
|
||||
// validate source and destination options
|
||||
contains := func(val string, list []string) (_ bool) {
|
||||
for _, l := range list {
|
||||
if l == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if len(opts.source) == 0 {
|
||||
return errors.New("A source transport must be specified")
|
||||
}
|
||||
if !slices.Contains([]string{docker.Transport.Name(), directory.Transport.Name(), "yaml"}, opts.source) {
|
||||
if !contains(opts.source, []string{docker.Transport.Name(), directory.Transport.Name(), "yaml"}) {
|
||||
return fmt.Errorf("%q is not a valid source transport", opts.source)
|
||||
}
|
||||
|
||||
if len(opts.destination) == 0 {
|
||||
return errors.New("A destination transport must be specified")
|
||||
}
|
||||
if !slices.Contains([]string{docker.Transport.Name(), directory.Transport.Name()}, opts.destination) {
|
||||
if !contains(opts.destination, []string{docker.Transport.Name(), directory.Transport.Name()}) {
|
||||
return fmt.Errorf("%q is not a valid destination transport", opts.destination)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var _ yaml.Unmarshaler = (*tlsVerifyConfig)(nil)
|
||||
|
||||
func TestTLSVerifyConfig(t *testing.T) {
|
||||
type container struct { // An example of a larger config file
|
||||
TLSVerify tlsVerifyConfig `yaml:"tls-verify"`
|
||||
}
|
||||
|
||||
for _, c := range []struct {
|
||||
input string
|
||||
expected tlsVerifyConfig
|
||||
}{
|
||||
{
|
||||
input: `tls-verify: true`,
|
||||
expected: tlsVerifyConfig{skip: types.OptionalBoolFalse},
|
||||
},
|
||||
{
|
||||
input: `tls-verify: false`,
|
||||
expected: tlsVerifyConfig{skip: types.OptionalBoolTrue},
|
||||
},
|
||||
{
|
||||
input: ``, // No value
|
||||
expected: tlsVerifyConfig{skip: types.OptionalBoolUndefined},
|
||||
},
|
||||
} {
|
||||
config := container{}
|
||||
err := yaml.Unmarshal([]byte(c.input), &config)
|
||||
require.NoError(t, err, c.input)
|
||||
assert.Equal(t, c.expected, config.TLSVerify, c.input)
|
||||
}
|
||||
|
||||
// Invalid input
|
||||
config := container{}
|
||||
err := yaml.Unmarshal([]byte(`tls-verify: "not a valid bool"`), &config)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
"github.com/syndtr/gocapability/capability"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var neededCapabilities = []capability.Cap{
|
||||
@@ -22,32 +21,29 @@ func maybeReexec() error {
|
||||
// With Skopeo we need only the subset of the root capabilities necessary
|
||||
// for pulling an image to the storage. Do not attempt to create a namespace
|
||||
// if we already have the capabilities we need.
|
||||
capabilities, err := capability.NewPid2(0)
|
||||
capabilities, err := capability.NewPid(0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading the current capabilities sets: %w", err)
|
||||
}
|
||||
if err := capabilities.Load(); err != nil {
|
||||
return fmt.Errorf("error loading the current capabilities sets: %w", err)
|
||||
}
|
||||
if slices.ContainsFunc(neededCapabilities, func(cap capability.Cap) bool {
|
||||
return !capabilities.Get(capability.EFFECTIVE, cap)
|
||||
}) {
|
||||
// We miss a capability we need, create a user namespaces
|
||||
unshare.MaybeReexecUsingUserNamespace(true)
|
||||
return nil
|
||||
for _, cap := range neededCapabilities {
|
||||
if !capabilities.Get(capability.EFFECTIVE, cap) {
|
||||
// We miss a capability we need, create a user namespaces
|
||||
unshare.MaybeReexecUsingUserNamespace(true)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func reexecIfNecessaryForImages(imageNames ...string) error {
|
||||
// Check if container-storage is used before doing unshare
|
||||
if slices.ContainsFunc(imageNames, func(imageName string) bool {
|
||||
for _, imageName := range imageNames {
|
||||
transport := alltransports.TransportFromImageName(imageName)
|
||||
// Hard-code the storage name to avoid a reference on c/image/storage.
|
||||
// See https://github.com/containers/skopeo/issues/771#issuecomment-563125006.
|
||||
return transport != nil && transport.Name() == "containers-storage"
|
||||
}) {
|
||||
return maybeReexec()
|
||||
if transport != nil && transport.Name() == "containers-storage" {
|
||||
return maybeReexec()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func noteCloseFailure(err error, description string, closeErr error) error {
|
||||
if err == nil {
|
||||
return fmt.Errorf("%s: %w", description, closeErr)
|
||||
}
|
||||
// In this case we prioritize the primary error for use with %w; closeErr is usually less relevant, or might be a consequence of the primary error.
|
||||
// In this case we prioritize the primary error for use with %w; closeErr is usually less relevant, or might be a consequence of the primary erorr.
|
||||
return fmt.Errorf("%w (%s: %v)", err, description, closeErr)
|
||||
}
|
||||
|
||||
@@ -315,11 +315,14 @@ func parseCreds(creds string) (string, string, error) {
|
||||
if creds == "" {
|
||||
return "", "", errors.New("credentials can't be empty")
|
||||
}
|
||||
username, password, _ := strings.Cut(creds, ":") // Sets password to "" if there is no ":"
|
||||
if username == "" {
|
||||
up := strings.SplitN(creds, ":", 2)
|
||||
if len(up) == 1 {
|
||||
return up[0], "", nil
|
||||
}
|
||||
if up[0] == "" {
|
||||
return "", "", errors.New("username can't be empty")
|
||||
}
|
||||
return username, password, nil
|
||||
return up[0], up[1], nil
|
||||
}
|
||||
|
||||
func getDockerAuth(creds string) (*types.DockerAuthConfig, error) {
|
||||
|
||||
@@ -385,6 +385,7 @@ func TestParseManifestFormat(t *testing.T) {
|
||||
// since there is a shared authfile image option and a non-shared (prefixed) one, make sure the override logic
|
||||
// works correctly.
|
||||
func TestImageOptionsAuthfileOverride(t *testing.T) {
|
||||
|
||||
for _, testCase := range []struct {
|
||||
flagPrefix string
|
||||
cmdFlags []string
|
||||
|
||||
@@ -6,6 +6,17 @@
|
||||
|
||||
set -e
|
||||
|
||||
_EOL=20270501
|
||||
if [[ $(date +%Y%m%d) -ge $_EOL ]]; then
|
||||
die "As of $_EOL this branch is probably
|
||||
no longer supported in RHEL 9.2/8.8, please
|
||||
confirm this with RHEL Program Management. If so:
|
||||
It should be removed from Cirrus-Cron,
|
||||
the .cirrus.yml file removed, and
|
||||
the VM images (manually) unmarked
|
||||
'permanent=true'"
|
||||
fi
|
||||
|
||||
# BEGIN Global export of all variables
|
||||
set -a
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[comment]: <> (***ATTENTION*** ***WARNING*** ***ALERT*** ***CAUTION*** ***DANGER***)
|
||||
[comment]: <> ()
|
||||
[comment]: <> (ANY changes made to this file, once committed/merged must)
|
||||
[comment]: <> (ANY changes made to this file, once commited/merged must)
|
||||
[comment]: <> (be manually copy/pasted -in markdown- into the description)
|
||||
[comment]: <> (field on Quay at the following locations:)
|
||||
[comment]: <> ()
|
||||
|
||||
@@ -20,8 +20,6 @@ automatically inherit any parts of the source name.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--additional-tag**=_strings_
|
||||
|
||||
Additional tags (supports docker-archive).
|
||||
|
||||
@@ -31,8 +31,6 @@ $ docker exec -it registry /usr/bin/registry garbage-collect /etc/docker-distrib
|
||||
|
||||
## OPTIONS
|
||||
|
||||
See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--authfile** _path_
|
||||
|
||||
Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`.
|
||||
|
||||
@@ -17,8 +17,6 @@ The private key is written to _prefix_**.pub** .
|
||||
|
||||
## OPTIONS
|
||||
|
||||
See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--help**, **-h**
|
||||
|
||||
Print usage statement
|
||||
|
||||
@@ -17,8 +17,6 @@ To see values for a different architecture/OS, use the **--override-os** / **--o
|
||||
|
||||
## OPTIONS
|
||||
|
||||
See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--authfile** _path_
|
||||
|
||||
Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`.
|
||||
@@ -44,7 +42,6 @@ Use docker daemon host at _host_ (`docker-daemon:` transport only)
|
||||
|
||||
Format the output using the given Go template.
|
||||
The keys of the returned JSON can be used as the values for the --format flag (see examples below).
|
||||
Supports the Go templating functions available at https://pkg.go.dev/github.com/containers/common/pkg/report#hdr-Template_Functions
|
||||
|
||||
**--help**, **-h**
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ Return a list of tags from _source-image_ in a registry or a local docker-archiv
|
||||
|
||||
## OPTIONS
|
||||
|
||||
See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--authfile** _path_
|
||||
|
||||
Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`.
|
||||
|
||||
@@ -15,8 +15,6 @@ flag. The default path used is **${XDG\_RUNTIME\_DIR}/containers/auth.json**.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--password**, **-p**=*password*
|
||||
|
||||
Password for registry
|
||||
|
||||
@@ -14,8 +14,6 @@ All the cached credentials can be removed by setting the **all** flag.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--authfile**=*path*
|
||||
|
||||
Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json
|
||||
|
||||
@@ -17,8 +17,6 @@ This is primarily a debugging tool, useful for special cases, and usually should
|
||||
|
||||
## OPTIONS
|
||||
|
||||
See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--help**, **-h**
|
||||
|
||||
Print usage statement
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
skopeo\-standalone\-verify - Verify an image signature.
|
||||
|
||||
## SYNOPSIS
|
||||
**skopeo standalone-verify** _manifest_ _docker-reference_ _key-fingerprints_ _signature_
|
||||
**skopeo standalone-verify** _manifest_ _docker-reference_ _key-fingerprint_ _signature_
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -16,7 +16,7 @@ as per containers-policy.json(5).
|
||||
|
||||
_docker-reference_ A docker reference expected to identify the image in the signature
|
||||
|
||||
_key-fingerprints_ Identities of trusted signing keys (comma separated), or "any" to trust any known key when using a public key file
|
||||
_key-fingerprint_ Expected identity of the signing key
|
||||
|
||||
_signature_ Path to signature file
|
||||
|
||||
@@ -24,16 +24,10 @@ as per containers-policy.json(5).
|
||||
|
||||
## OPTIONS
|
||||
|
||||
See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--help**, **-h**
|
||||
|
||||
Print usage statement
|
||||
|
||||
**--public-key-file** _public key file_
|
||||
|
||||
File containing the public keys to use when verifying signatures. If this is not specified, keys from the GPG homedir are used.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
```console
|
||||
|
||||
@@ -8,7 +8,10 @@ skopeo\-sync - Synchronize images between registry repositories and local direct
|
||||
**skopeo sync** [*options*] --src _transport_ --dest _transport_ _source_ _destination_
|
||||
|
||||
## DESCRIPTION
|
||||
Synchronize images between registry repositories and local directories. Synchronization is achieved by copying all the images found at _source_ to _destination_ - useful when synchronizing a local container registry mirror or for populating registries running inside of air-gapped environments.
|
||||
Synchronize images between registry repoositories and local directories.
|
||||
The synchronization is achieved by copying all the images found at _source_ to _destination_.
|
||||
|
||||
Useful to synchronize a local container registry mirror, and to to populate registries running inside of air-gapped environments.
|
||||
|
||||
Differently from other skopeo commands, skopeo sync requires both source and destination transports to be specified separately from _source_ and _destination_.
|
||||
One of the problems of prefixing a destination with its transport is that, the registry `docker://hostname:port` would be wrongly interpreted as an image reference at a non-fully qualified registry, with `hostname` and `port` the image name and tag.
|
||||
@@ -29,9 +32,6 @@ When the `--scoped` option is specified, images are prefixed with the source ima
|
||||
name can be stored at _destination_.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--all**, **-a**
|
||||
If one of the images in __src__ refers to a list of images, instead of copying just the image which matches the current OS and
|
||||
architecture (subject to the use of the global --override-os, --override-arch and --override-variant options), attempt to copy all of
|
||||
|
||||
@@ -51,9 +51,6 @@ See [containers-transports(5)](https://github.com/containers/image/blob/main/doc
|
||||
|
||||
## OPTIONS
|
||||
|
||||
These options should be placed before the subcommand name.
|
||||
Individual subcommands have their own options.
|
||||
|
||||
**--command-timeout** _duration_
|
||||
|
||||
Timeout for the command execution.
|
||||
|
||||
120
go.mod
120
go.mod
@@ -1,84 +1,86 @@
|
||||
module github.com/containers/skopeo
|
||||
|
||||
go 1.18
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/containers/common v0.52.0
|
||||
github.com/containers/image/v5 v5.25.0
|
||||
github.com/containers/ocicrypt v1.1.7
|
||||
github.com/containers/storage v1.46.1
|
||||
github.com/containers/common v0.51.4
|
||||
github.com/containers/image/v5 v5.24.3
|
||||
github.com/containers/ocicrypt v1.1.10
|
||||
github.com/containers/storage v1.45.3
|
||||
github.com/docker/distribution v2.8.1+incompatible
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2
|
||||
github.com/opencontainers/image-tools v1.0.0-rc3
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||
golang.org/x/term v0.7.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
golang.org/x/term v0.17.0
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/Microsoft/hcsshim v0.10.0-rc.7 // indirect
|
||||
github.com/Microsoft/hcsshim v0.9.6 // indirect
|
||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||
github.com/containerd/cgroups v1.0.4 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.13.0 // indirect
|
||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.5.0 // indirect
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/docker v23.0.3+incompatible // indirect
|
||||
github.com/docker/docker v20.10.23+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.7.0 // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
||||
github.com/go-openapi/errors v0.20.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/loads v0.21.2 // indirect
|
||||
github.com/go-openapi/runtime v0.25.0 // indirect
|
||||
github.com/go-openapi/spec v0.20.8 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.7 // indirect
|
||||
github.com/go-openapi/runtime v0.24.1 // indirect
|
||||
github.com/go-openapi/spec v0.20.7 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.3 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-openapi/validate v0.22.1 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.12.0 // indirect
|
||||
github.com/go-openapi/validate v0.22.0 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-containerregistry v0.13.0 // indirect
|
||||
github.com/google/go-intervals v0.0.2 // indirect
|
||||
github.com/google/trillian v1.5.1 // indirect
|
||||
github.com/google/trillian v1.5.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.16.4 // indirect
|
||||
github.com/klauspost/compress v1.15.15 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8 // indirect
|
||||
github.com/leodido/go-urn v1.2.2 // indirect
|
||||
github.com/letsencrypt/boulder v0.0.0-20230213213521-fdfea0d469b6 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/letsencrypt/boulder v0.0.0-20230130200452-c091e64aa391 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
@@ -86,51 +88,49 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/opencontainers/runc v1.1.5 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect
|
||||
github.com/opencontainers/selinux v1.11.0 // indirect
|
||||
github.com/opencontainers/runc v1.1.4 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb // indirect
|
||||
github.com/opencontainers/selinux v1.10.2 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/proglottis/gpgme v0.1.3 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/rivo/uniseg v0.4.3 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
||||
github.com/russross/blackfriday v2.0.0+incompatible // indirect
|
||||
github.com/segmentio/ksuid v1.0.4 // indirect
|
||||
github.com/sigstore/fulcio v1.2.0 // indirect
|
||||
github.com/sigstore/rekor v1.1.0 // indirect
|
||||
github.com/sigstore/sigstore v1.6.0 // indirect
|
||||
github.com/sigstore/fulcio v1.0.0 // indirect
|
||||
github.com/sigstore/rekor v1.0.1 // indirect
|
||||
github.com/sigstore/sigstore v1.5.2 // indirect
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect
|
||||
github.com/sylabs/sif/v2 v2.11.1 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
||||
github.com/theupdateframework/go-tuf v0.5.2 // indirect
|
||||
github.com/sylabs/sif/v2 v2.9.0 // indirect
|
||||
github.com/tchap/go-patricia v2.3.0+incompatible // indirect
|
||||
github.com/theupdateframework/go-tuf v0.5.2-0.20221207161717-9cb61d6e65f5 // indirect
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/vbatts/tar-split v0.11.3 // indirect
|
||||
github.com/vbauerster/mpb/v8 v8.3.0 // indirect
|
||||
github.com/vbatts/tar-split v0.11.2 // indirect
|
||||
github.com/vbauerster/mpb/v7 v7.5.3 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
go.mongodb.org/mongo-driver v1.11.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.mongodb.org/mongo-driver v1.11.1 // indirect
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/otel v1.13.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.13.0 // indirect
|
||||
golang.org/x/crypto v0.8.0 // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/net v0.9.0 // indirect
|
||||
golang.org/x/oauth2 v0.6.0 // indirect
|
||||
golang.org/x/crypto v0.19.0 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.7.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
|
||||
google.golang.org/grpc v1.54.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
google.golang.org/grpc v1.56.3 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/go-jose/go-jose.v2 v2.6.1 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
92
hack/make.sh
Executable file
92
hack/make.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# This script builds various binary from a checkout of the skopeo
|
||||
# source code. DO NOT CALL THIS SCRIPT DIRECTLY.
|
||||
#
|
||||
# Requirements:
|
||||
# - The current directory should be a checkout of the skopeo source code
|
||||
# (https://github.com/containers/skopeo). Whatever version is checked out
|
||||
# will be built.
|
||||
# - The script is intended to be run inside the container specified
|
||||
# in the output of hack/get_fqin.sh
|
||||
# - The right way to call this script is to invoke "make" from
|
||||
# your checkout of the skopeo repository.
|
||||
# the Makefile will do a "docker build -t skopeo ." and then
|
||||
# "docker run hack/make.sh" in the resulting image.
|
||||
#
|
||||
|
||||
set -o pipefail
|
||||
|
||||
export SKOPEO_PKG='github.com/containers/skopeo'
|
||||
export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
export MAKEDIR="$SCRIPTDIR/make"
|
||||
|
||||
# Set this to 1 to enable installation/modification of environment/services
|
||||
export SKOPEO_CONTAINER_TESTS=${SKOPEO_CONTAINER_TESTS:-0}
|
||||
|
||||
if [[ "$SKOPEO_CONTAINER_TESTS" == "0" ]] && [[ "$CI" != "true" ]]; then
|
||||
(
|
||||
echo "***************************************************************"
|
||||
echo "WARNING: Executing tests directly on the local development"
|
||||
echo " host is highly discouraged. Many important items"
|
||||
echo " will be skipped. For manual execution, please utilize"
|
||||
echo " the Makefile targets WITHOUT the '-local' suffix."
|
||||
echo "***************************************************************"
|
||||
) > /dev/stderr
|
||||
sleep 5
|
||||
fi
|
||||
|
||||
echo
|
||||
|
||||
# List of bundles to create when no argument is passed
|
||||
# TODO(runcom): these are the one left from Docker...for now
|
||||
# test-unit
|
||||
# validate-dco
|
||||
# cover
|
||||
DEFAULT_BUNDLES=(
|
||||
validate-gofmt
|
||||
validate-lint
|
||||
validate-vet
|
||||
validate-git-marks
|
||||
|
||||
test-integration
|
||||
)
|
||||
|
||||
# Go module support: set `-mod=vendor` to use the vendored sources
|
||||
# See also the top-level Makefile.
|
||||
mod_vendor=
|
||||
if go help mod >/dev/null 2>&1; then
|
||||
export GO111MODULE=on
|
||||
mod_vendor='-mod=vendor'
|
||||
fi
|
||||
|
||||
go_test_dir() {
|
||||
dir=$1
|
||||
(
|
||||
echo '+ go test' $mod_vendor $TESTFLAGS ${BUILDTAGS:+-tags "$BUILDTAGS"} "${SKOPEO_PKG}${dir#.}"
|
||||
cd "$dir"
|
||||
export DEST="$ABS_DEST" # we're in a subshell, so this is safe -- our integration-cli tests need DEST, and "cd" screws it up
|
||||
go test $mod_vendor $TESTFLAGS ${BUILDTAGS:+-tags "$BUILDTAGS"}
|
||||
)
|
||||
}
|
||||
|
||||
bundle() {
|
||||
local bundle="$1"; shift
|
||||
echo "---> Making bundle: $(basename "$bundle")"
|
||||
source "$SCRIPTDIR/make/$bundle" "$@"
|
||||
}
|
||||
|
||||
main() {
|
||||
if [ $# -lt 1 ]; then
|
||||
bundles=(${DEFAULT_BUNDLES[@]})
|
||||
else
|
||||
bundles=($@)
|
||||
fi
|
||||
for bundle in ${bundles[@]}; do
|
||||
bundle "$bundle"
|
||||
echo
|
||||
done
|
||||
}
|
||||
|
||||
main "$@"
|
||||
31
hack/make/.validate
Normal file
31
hack/make/.validate
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$VALIDATE_UPSTREAM" ]; then
|
||||
# this is kind of an expensive check, so let's not do this twice if we
|
||||
# are running more than one validate bundlescript
|
||||
|
||||
VALIDATE_REPO='https://github.com/containers/skopeo.git'
|
||||
VALIDATE_BRANCH='main'
|
||||
|
||||
if [ "$TRAVIS" = 'true' -a "$TRAVIS_PULL_REQUEST" != 'false' ]; then
|
||||
VALIDATE_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git"
|
||||
VALIDATE_BRANCH="${TRAVIS_BRANCH}"
|
||||
fi
|
||||
|
||||
VALIDATE_HEAD="$(git rev-parse --verify HEAD)"
|
||||
|
||||
git fetch -q "$VALIDATE_REPO" "refs/heads/$VALIDATE_BRANCH"
|
||||
VALIDATE_UPSTREAM="$(git rev-parse --verify FETCH_HEAD)"
|
||||
|
||||
VALIDATE_COMMIT_LOG="$VALIDATE_UPSTREAM..$VALIDATE_HEAD"
|
||||
VALIDATE_COMMIT_DIFF="$VALIDATE_UPSTREAM...$VALIDATE_HEAD"
|
||||
|
||||
validate_diff() {
|
||||
git diff "$VALIDATE_UPSTREAM" "$@"
|
||||
}
|
||||
validate_log() {
|
||||
if [ "$VALIDATE_UPSTREAM" != "$VALIDATE_HEAD" ]; then
|
||||
git log "$VALIDATE_COMMIT_LOG" "$@"
|
||||
fi
|
||||
}
|
||||
fi
|
||||
12
hack/make/test-integration
Executable file
12
hack/make/test-integration
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
bundle_test_integration() {
|
||||
go_test_dir ./integration
|
||||
}
|
||||
|
||||
# subshell so that we can export PATH without breaking other things
|
||||
(
|
||||
make PREFIX=/usr install
|
||||
bundle_test_integration
|
||||
) 2>&1
|
||||
44
hack/make/validate-git-marks
Executable file
44
hack/make/validate-git-marks
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
source "$(dirname "$BASH_SOURCE")/.validate"
|
||||
|
||||
# folders=$(find * -type d | egrep -v '^Godeps|bundles|.git')
|
||||
|
||||
IFS=$'\n'
|
||||
files=( $(validate_diff --diff-filter=ACMR --name-only -- '*' | grep -v '^vendor/' || true) )
|
||||
unset IFS
|
||||
|
||||
badFiles=()
|
||||
for f in "${files[@]}"; do
|
||||
if [ $(grep -r "^<<<<<<<" $f) ]; then
|
||||
badFiles+=( "$f" )
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ $(grep -r "^>>>>>>>" $f) ]; then
|
||||
badFiles+=( "$f" )
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ $(grep -r "^=======$" $f) ]; then
|
||||
badFiles+=( "$f" )
|
||||
continue
|
||||
fi
|
||||
set -e
|
||||
done
|
||||
|
||||
|
||||
if [ ${#badFiles[@]} -eq 0 ]; then
|
||||
echo 'Congratulations! There is no conflict.'
|
||||
else
|
||||
{
|
||||
echo "There is trace of conflict(s) in the following files :"
|
||||
for f in "${badFiles[@]}"; do
|
||||
echo " - $f"
|
||||
done
|
||||
echo
|
||||
echo 'Please fix the conflict(s) commit the result.'
|
||||
echo
|
||||
} >&2
|
||||
false
|
||||
fi
|
||||
@@ -1,7 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
source "$(dirname "$BASH_SOURCE")/.validate"
|
||||
|
||||
IFS=$'\n'
|
||||
files=( $(find . -name '*.go' | grep -v '^./vendor/' | sort || true) )
|
||||
files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^vendor/' || true) )
|
||||
unset IFS
|
||||
|
||||
badFiles=()
|
||||
@@ -23,5 +25,5 @@ else
|
||||
echo 'Please reformat the above files using "gofmt -s -w" and commit the result.'
|
||||
echo
|
||||
} >&2
|
||||
exit 1
|
||||
false
|
||||
fi
|
||||
33
hack/make/validate-lint
Executable file
33
hack/make/validate-lint
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
source "$(dirname "$BASH_SOURCE")/.validate"
|
||||
|
||||
# We will eventually get to the point where packages should be the complete list
|
||||
# of subpackages, vendoring excluded, as given by:
|
||||
#
|
||||
IFS=$'\n'
|
||||
files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^vendor/\|^integration' || true) )
|
||||
unset IFS
|
||||
|
||||
errors=()
|
||||
for f in "${files[@]}"; do
|
||||
failedLint=$(golint "$f")
|
||||
if [ "$failedLint" ]; then
|
||||
errors+=( "$failedLint" )
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#errors[@]} -eq 0 ]; then
|
||||
echo 'Congratulations! All Go source files have been linted.'
|
||||
else
|
||||
{
|
||||
echo "Errors from golint:"
|
||||
for err in "${errors[@]}"; do
|
||||
echo "$err"
|
||||
done
|
||||
echo
|
||||
echo 'Please fix the above errors. You can test via "golint" and commit the result.'
|
||||
echo
|
||||
} >&2
|
||||
false
|
||||
fi
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
errors=$(go vet -tags="${BUILDTAGS}" ./... 2>&1)
|
||||
errors=$(go vet -tags="${BUILDTAGS}" $mod_vendor $(go list $mod_vendor -e ./...))
|
||||
|
||||
if [ -z "$errors" ]; then
|
||||
echo 'Congratulations! All Go source files have been vetted.'
|
||||
@@ -12,5 +12,5 @@ else
|
||||
echo 'Please fix the above errors. You can test via "go vet" and commit the result.'
|
||||
echo
|
||||
} >&2
|
||||
exit 1
|
||||
false
|
||||
fi
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
make PREFIX=/usr install
|
||||
|
||||
echo "cd ./integration;" go test $TESTFLAGS ${BUILDTAGS:+-tags "$BUILDTAGS"}
|
||||
cd ./integration
|
||||
go test $TESTFLAGS ${BUILDTAGS:+-tags "$BUILDTAGS"}
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
IFS=$'\n'
|
||||
files=( $(git ls-tree -r HEAD --name-only | grep -v '^vendor/' || true) )
|
||||
unset IFS
|
||||
|
||||
badFiles=()
|
||||
for f in "${files[@]}"; do
|
||||
if [ $(grep -r "^\(<<<<<<<\|>>>>>>>\|^=======$\)" $f) ]; then
|
||||
badFiles+=( "$f" )
|
||||
continue
|
||||
fi
|
||||
set -e
|
||||
done
|
||||
|
||||
|
||||
if [ ${#badFiles[@]} -eq 0 ]; then
|
||||
echo 'Congratulations! There is no conflict.'
|
||||
else
|
||||
{
|
||||
echo "There is trace of conflict(s) in the following files :"
|
||||
for f in "${badFiles[@]}"; do
|
||||
echo " - $f"
|
||||
done
|
||||
echo
|
||||
echo 'Please fix the conflict(s) commit the result.'
|
||||
echo
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
errors=$($GOBIN/golangci-lint run --build-tags "${BUILDTAGS}" 2>&1)
|
||||
|
||||
if [ -z "$errors" ]; then
|
||||
echo 'Congratulations! All Go source files have been linted.'
|
||||
else
|
||||
{
|
||||
echo "Errors from golangci-lint:"
|
||||
echo "$errors"
|
||||
echo
|
||||
echo 'Please fix the above errors. You can test via "golangci-lint" and commit the result.'
|
||||
echo
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# Set this to 1 to enable installation/modification of environment/services
|
||||
export SKOPEO_CONTAINER_TESTS=${SKOPEO_CONTAINER_TESTS:-0}
|
||||
|
||||
if [[ "$SKOPEO_CONTAINER_TESTS" == "0" ]] && [[ "$CI" != "true" ]]; then
|
||||
(
|
||||
echo "***************************************************************"
|
||||
echo "WARNING: Executing tests directly on the local development"
|
||||
echo " host is highly discouraged. Many important items"
|
||||
echo " will be skipped. For manual execution, please utilize"
|
||||
echo " the Makefile targets WITHOUT the '-local' suffix."
|
||||
echo "***************************************************************"
|
||||
) > /dev/stderr
|
||||
sleep 5
|
||||
fi
|
||||
@@ -1,34 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
const blockedRegistriesConf = "./fixtures/blocked-registries.conf"
|
||||
const blockedErrorRegex = `.*registry registry-blocked.com is blocked in .*`
|
||||
|
||||
func (s *skopeoSuite) TestCopyBlockedSource() {
|
||||
t := s.T()
|
||||
assertSkopeoFails(t, blockedErrorRegex,
|
||||
func (s *SkopeoSuite) TestCopyBlockedSource(c *check.C) {
|
||||
assertSkopeoFails(c, blockedErrorRegex,
|
||||
"--registries-conf", blockedRegistriesConf, "copy",
|
||||
"docker://registry-blocked.com/image:test",
|
||||
"docker://registry-unblocked.com/image:test")
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TestCopyBlockedDestination() {
|
||||
t := s.T()
|
||||
assertSkopeoFails(t, blockedErrorRegex,
|
||||
func (s *SkopeoSuite) TestCopyBlockedDestination(c *check.C) {
|
||||
assertSkopeoFails(c, blockedErrorRegex,
|
||||
"--registries-conf", blockedRegistriesConf, "copy",
|
||||
"docker://registry-unblocked.com/image:test",
|
||||
"docker://registry-blocked.com/image:test")
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TestInspectBlocked() {
|
||||
t := s.T()
|
||||
assertSkopeoFails(t, blockedErrorRegex,
|
||||
func (s *SkopeoSuite) TestInspectBlocked(c *check.C) {
|
||||
assertSkopeoFails(c, blockedErrorRegex,
|
||||
"--registries-conf", blockedRegistriesConf, "inspect",
|
||||
"docker://registry-blocked.com/image:test")
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TestDeleteBlocked() {
|
||||
t := s.T()
|
||||
assertSkopeoFails(t, blockedErrorRegex,
|
||||
func (s *SkopeoSuite) TestDeleteBlocked(c *check.C) {
|
||||
assertSkopeoFails(c, blockedErrorRegex,
|
||||
"--registries-conf", blockedRegistriesConf, "delete",
|
||||
"docker://registry-blocked.com/image:test")
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/containers/skopeo/version"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -16,104 +14,98 @@ const (
|
||||
privateRegistryURL1 = "127.0.0.1:5001"
|
||||
)
|
||||
|
||||
func TestSkopeo(t *testing.T) {
|
||||
suite.Run(t, &skopeoSuite{})
|
||||
func Test(t *testing.T) {
|
||||
check.TestingT(t)
|
||||
}
|
||||
|
||||
type skopeoSuite struct {
|
||||
suite.Suite
|
||||
func init() {
|
||||
check.Suite(&SkopeoSuite{})
|
||||
}
|
||||
|
||||
type SkopeoSuite struct {
|
||||
regV2 *testRegistryV2
|
||||
regV2WithAuth *testRegistryV2
|
||||
}
|
||||
|
||||
var _ = suite.SetupAllSuite(&skopeoSuite{})
|
||||
var _ = suite.TearDownAllSuite(&skopeoSuite{})
|
||||
|
||||
func (s *skopeoSuite) SetupSuite() {
|
||||
t := s.T()
|
||||
func (s *SkopeoSuite) SetUpSuite(c *check.C) {
|
||||
_, err := exec.LookPath(skopeoBinary)
|
||||
require.NoError(t, err)
|
||||
s.regV2 = setupRegistryV2At(t, privateRegistryURL0, false, false)
|
||||
s.regV2WithAuth = setupRegistryV2At(t, privateRegistryURL1, true, false)
|
||||
c.Assert(err, check.IsNil)
|
||||
s.regV2 = setupRegistryV2At(c, privateRegistryURL0, false, false)
|
||||
s.regV2WithAuth = setupRegistryV2At(c, privateRegistryURL1, true, false)
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TearDownSuite() {
|
||||
func (s *SkopeoSuite) TearDownSuite(c *check.C) {
|
||||
if s.regV2 != nil {
|
||||
s.regV2.tearDown()
|
||||
s.regV2.tearDown(c)
|
||||
}
|
||||
if s.regV2WithAuth != nil {
|
||||
// cmd := exec.Command("docker", "logout", s.regV2WithAuth)
|
||||
// require.Noerror(t, cmd.Run())
|
||||
s.regV2WithAuth.tearDown()
|
||||
//cmd := exec.Command("docker", "logout", s.regV2WithAuth)
|
||||
//c.Assert(cmd.Run(), check.IsNil)
|
||||
s.regV2WithAuth.tearDown(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TestVersion() {
|
||||
t := s.T()
|
||||
assertSkopeoSucceeds(t, fmt.Sprintf(".*%s version %s.*", skopeoBinary, version.Version),
|
||||
// TODO like dockerCmd but much easier, just out,err
|
||||
//func skopeoCmd()
|
||||
|
||||
func (s *SkopeoSuite) TestVersion(c *check.C) {
|
||||
assertSkopeoSucceeds(c, fmt.Sprintf(".*%s version %s.*", skopeoBinary, version.Version),
|
||||
"--version")
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TestCanAuthToPrivateRegistryV2WithoutDockerCfg() {
|
||||
t := s.T()
|
||||
assertSkopeoFails(t, ".*manifest unknown.*",
|
||||
func (s *SkopeoSuite) TestCanAuthToPrivateRegistryV2WithoutDockerCfg(c *check.C) {
|
||||
assertSkopeoFails(c, ".*manifest unknown.*",
|
||||
"--tls-verify=false", "inspect", "--creds="+s.regV2WithAuth.username+":"+s.regV2WithAuth.password, fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TestNeedAuthToPrivateRegistryV2WithoutDockerCfg() {
|
||||
t := s.T()
|
||||
assertSkopeoFails(t, ".*authentication required.*",
|
||||
func (s *SkopeoSuite) TestNeedAuthToPrivateRegistryV2WithoutDockerCfg(c *check.C) {
|
||||
assertSkopeoFails(c, ".*authentication required.*",
|
||||
"--tls-verify=false", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TestCertDirInsteadOfCertPath() {
|
||||
t := s.T()
|
||||
assertSkopeoFails(t, ".*unknown flag: --cert-path.*",
|
||||
func (s *SkopeoSuite) TestCertDirInsteadOfCertPath(c *check.C) {
|
||||
assertSkopeoFails(c, ".*unknown flag: --cert-path.*",
|
||||
"--tls-verify=false", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url), "--cert-path=/")
|
||||
assertSkopeoFails(t, ".*authentication required.*",
|
||||
assertSkopeoFails(c, ".*authentication required.*",
|
||||
"--tls-verify=false", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url), "--cert-dir=/etc/docker/certs.d/")
|
||||
}
|
||||
|
||||
// TODO(runcom): as soon as we can push to registries ensure you can inspect here
|
||||
// not just get image not found :)
|
||||
func (s *skopeoSuite) TestNoNeedAuthToPrivateRegistryV2ImageNotFound() {
|
||||
t := s.T()
|
||||
func (s *SkopeoSuite) TestNoNeedAuthToPrivateRegistryV2ImageNotFound(c *check.C) {
|
||||
out, err := exec.Command(skopeoBinary, "--tls-verify=false", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2.url)).CombinedOutput()
|
||||
assert.Error(t, err, "%s", string(out))
|
||||
assert.Regexp(t, "(?s).*manifest unknown.*", string(out)) // (?s) : '.' will also match newlines
|
||||
assert.NotRegexp(t, "(?s).*unauthorized: authentication required.*", string(out)) // (?s) : '.' will also match newlines
|
||||
c.Assert(err, check.NotNil, check.Commentf(string(out)))
|
||||
c.Assert(string(out), check.Matches, "(?s).*manifest unknown.*") // (?s) : '.' will also match newlines
|
||||
c.Assert(string(out), check.Not(check.Matches), "(?s).*unauthorized: authentication required.*") // (?s) : '.' will also match newlines
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TestInspectFailsWhenReferenceIsInvalid() {
|
||||
t := s.T()
|
||||
assertSkopeoFails(t, `.*Invalid image name.*`, "inspect", "unknown")
|
||||
func (s *SkopeoSuite) TestInspectFailsWhenReferenceIsInvalid(c *check.C) {
|
||||
assertSkopeoFails(c, `.*Invalid image name.*`, "inspect", "unknown")
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TestLoginLogout() {
|
||||
t := s.T()
|
||||
assertSkopeoSucceeds(t, "^Login Succeeded!\n$",
|
||||
func (s *SkopeoSuite) TestLoginLogout(c *check.C) {
|
||||
assertSkopeoSucceeds(c, "^Login Succeeded!\n$",
|
||||
"login", "--tls-verify=false", "--username="+s.regV2WithAuth.username, "--password="+s.regV2WithAuth.password, s.regV2WithAuth.url)
|
||||
// test --get-login returns username
|
||||
assertSkopeoSucceeds(t, fmt.Sprintf("^%s\n$", s.regV2WithAuth.username),
|
||||
assertSkopeoSucceeds(c, fmt.Sprintf("^%s\n$", s.regV2WithAuth.username),
|
||||
"login", "--tls-verify=false", "--get-login", s.regV2WithAuth.url)
|
||||
// test logout
|
||||
assertSkopeoSucceeds(t, fmt.Sprintf("^Removed login credentials for %s\n$", s.regV2WithAuth.url),
|
||||
assertSkopeoSucceeds(c, fmt.Sprintf("^Removed login credentials for %s\n$", s.regV2WithAuth.url),
|
||||
"logout", s.regV2WithAuth.url)
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TestCopyWithLocalAuth() {
|
||||
t := s.T()
|
||||
assertSkopeoSucceeds(t, "^Login Succeeded!\n$",
|
||||
func (s *SkopeoSuite) TestCopyWithLocalAuth(c *check.C) {
|
||||
assertSkopeoSucceeds(c, "^Login Succeeded!\n$",
|
||||
"login", "--tls-verify=false", "--username="+s.regV2WithAuth.username, "--password="+s.regV2WithAuth.password, s.regV2WithAuth.url)
|
||||
// copy to private registry using local authentication
|
||||
imageName := fmt.Sprintf("docker://%s/busybox:mine", s.regV2WithAuth.url)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--dest-tls-verify=false", testFQIN+":latest", imageName)
|
||||
assertSkopeoSucceeds(c, "", "copy", "--dest-tls-verify=false", testFQIN+":latest", imageName)
|
||||
// inspect from private registry
|
||||
assertSkopeoSucceeds(t, "", "inspect", "--tls-verify=false", imageName)
|
||||
assertSkopeoSucceeds(c, "", "inspect", "--tls-verify=false", imageName)
|
||||
// logout from the registry
|
||||
assertSkopeoSucceeds(t, fmt.Sprintf("^Removed login credentials for %s\n$", s.regV2WithAuth.url),
|
||||
assertSkopeoSucceeds(c, fmt.Sprintf("^Removed login credentials for %s\n$", s.regV2WithAuth.url),
|
||||
"logout", s.regV2WithAuth.url)
|
||||
// inspect from private registry should fail after logout
|
||||
assertSkopeoFails(t, ".*authentication required.*",
|
||||
assertSkopeoFails(c, ".*authentication required.*",
|
||||
"inspect", "--tls-verify=false", imageName)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,11 +9,10 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containers/storage/pkg/homedir"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
var adminKUBECONFIG = map[string]string{
|
||||
@@ -31,21 +30,21 @@ type openshiftCluster struct {
|
||||
// startOpenshiftCluster creates a new openshiftCluster.
|
||||
// WARNING: This affects state in users' home directory! Only run
|
||||
// in isolated test environment.
|
||||
func startOpenshiftCluster(t *testing.T) *openshiftCluster {
|
||||
func startOpenshiftCluster(c *check.C) *openshiftCluster {
|
||||
cluster := &openshiftCluster{}
|
||||
cluster.workingDir = t.TempDir()
|
||||
cluster.workingDir = c.MkDir()
|
||||
|
||||
cluster.startMaster(t)
|
||||
cluster.prepareRegistryConfig(t)
|
||||
cluster.startRegistry(t)
|
||||
cluster.ocLoginToProject(t)
|
||||
cluster.dockerLogin(t)
|
||||
cluster.relaxImageSignerPermissions(t)
|
||||
cluster.startMaster(c)
|
||||
cluster.prepareRegistryConfig(c)
|
||||
cluster.startRegistry(c)
|
||||
cluster.ocLoginToProject(c)
|
||||
cluster.dockerLogin(c)
|
||||
cluster.relaxImageSignerPermissions(c)
|
||||
|
||||
return cluster
|
||||
}
|
||||
|
||||
// clusterCmd creates an exec.Cmd in cluster.workingDir with current environment modified by environment.
|
||||
// clusterCmd creates an exec.Cmd in cluster.workingDir with current environment modified by environment
|
||||
func (cluster *openshiftCluster) clusterCmd(env map[string]string, name string, args ...string) *exec.Cmd {
|
||||
cmd := exec.Command(name, args...)
|
||||
cmd.Dir = cluster.workingDir
|
||||
@@ -57,20 +56,21 @@ func (cluster *openshiftCluster) clusterCmd(env map[string]string, name string,
|
||||
}
|
||||
|
||||
// startMaster starts the OpenShift master (etcd+API server) and waits for it to be ready, or terminates on failure.
|
||||
func (cluster *openshiftCluster) startMaster(t *testing.T) {
|
||||
func (cluster *openshiftCluster) startMaster(c *check.C) {
|
||||
cmd := cluster.clusterCmd(nil, "openshift", "start", "master")
|
||||
cluster.processes = append(cluster.processes, cmd)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
// Send both to the same pipe. This might cause the two streams to be mixed up,
|
||||
// but logging actually goes only to stderr - this primarily ensure we log any
|
||||
// unexpected output to stdout.
|
||||
cmd.Stderr = cmd.Stdout
|
||||
err = cmd.Start()
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
portOpen, terminatePortCheck := newPortChecker(t, 8443)
|
||||
portOpen, terminatePortCheck := newPortChecker(c, 8443)
|
||||
defer func() {
|
||||
t.Logf("Terminating port check")
|
||||
c.Logf("Terminating port check")
|
||||
terminatePortCheck <- true
|
||||
}()
|
||||
|
||||
@@ -78,12 +78,12 @@ func (cluster *openshiftCluster) startMaster(t *testing.T) {
|
||||
logCheckFound := make(chan bool)
|
||||
go func() {
|
||||
defer func() {
|
||||
t.Logf("Log checker exiting")
|
||||
c.Logf("Log checker exiting")
|
||||
}()
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
t.Logf("Log line: %s", line)
|
||||
c.Logf("Log line: %s", line)
|
||||
if strings.Contains(line, "Started Origin Controllers") {
|
||||
logCheckFound <- true
|
||||
return
|
||||
@@ -92,7 +92,7 @@ func (cluster *openshiftCluster) startMaster(t *testing.T) {
|
||||
// Note: we can block before we get here.
|
||||
select {
|
||||
case <-terminateLogCheck:
|
||||
t.Logf("terminated")
|
||||
c.Logf("terminated")
|
||||
return
|
||||
default:
|
||||
// Do not block here and read the next line.
|
||||
@@ -101,7 +101,7 @@ func (cluster *openshiftCluster) startMaster(t *testing.T) {
|
||||
logCheckFound <- false
|
||||
}()
|
||||
defer func() {
|
||||
t.Logf("Terminating log check")
|
||||
c.Logf("Terminating log check")
|
||||
terminateLogCheck <- true
|
||||
}()
|
||||
|
||||
@@ -110,26 +110,26 @@ func (cluster *openshiftCluster) startMaster(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||
defer cancel()
|
||||
for !gotPortCheck || !gotLogCheck {
|
||||
t.Logf("Waiting for master")
|
||||
c.Logf("Waiting for master")
|
||||
select {
|
||||
case <-portOpen:
|
||||
t.Logf("port check done")
|
||||
c.Logf("port check done")
|
||||
gotPortCheck = true
|
||||
case found := <-logCheckFound:
|
||||
t.Logf("log check done, found: %t", found)
|
||||
c.Logf("log check done, found: %t", found)
|
||||
if !found {
|
||||
t.Fatal("log check done, success message not found")
|
||||
c.Fatal("log check done, success message not found")
|
||||
}
|
||||
gotLogCheck = true
|
||||
case <-ctx.Done():
|
||||
t.Fatalf("Timed out waiting for master: %v", ctx.Err())
|
||||
c.Fatalf("Timed out waiting for master: %v", ctx.Err())
|
||||
}
|
||||
}
|
||||
t.Logf("OK, master started!")
|
||||
c.Logf("OK, master started!")
|
||||
}
|
||||
|
||||
// prepareRegistryConfig creates a registry service account and a related k8s client configuration in ${cluster.workingDir}/openshift.local.registry.
|
||||
func (cluster *openshiftCluster) prepareRegistryConfig(t *testing.T) {
|
||||
func (cluster *openshiftCluster) prepareRegistryConfig(c *check.C) {
|
||||
// This partially mimics the objects created by (oadm registry), except that we run the
|
||||
// server directly as an ordinary process instead of a pod with an implicitly attached service account.
|
||||
saJSON := `{
|
||||
@@ -140,93 +140,93 @@ func (cluster *openshiftCluster) prepareRegistryConfig(t *testing.T) {
|
||||
}
|
||||
}`
|
||||
cmd := cluster.clusterCmd(adminKUBECONFIG, "oc", "create", "-f", "-")
|
||||
runExecCmdWithInput(t, cmd, saJSON)
|
||||
runExecCmdWithInput(c, cmd, saJSON)
|
||||
|
||||
cmd = cluster.clusterCmd(adminKUBECONFIG, "oadm", "policy", "add-cluster-role-to-user", "system:registry", "-z", "registry")
|
||||
out, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, "%s", string(out))
|
||||
require.Equal(t, "cluster role \"system:registry\" added: \"registry\"\n", string(out))
|
||||
c.Assert(err, check.IsNil, check.Commentf("%s", string(out)))
|
||||
c.Assert(string(out), check.Equals, "cluster role \"system:registry\" added: \"registry\"\n")
|
||||
|
||||
cmd = cluster.clusterCmd(adminKUBECONFIG, "oadm", "create-api-client-config", "--client-dir=openshift.local.registry", "--basename=openshift-registry", "--user=system:serviceaccount:default:registry")
|
||||
out, err = cmd.CombinedOutput()
|
||||
require.NoError(t, err, "%s", string(out))
|
||||
require.Equal(t, "", string(out))
|
||||
c.Assert(err, check.IsNil, check.Commentf("%s", string(out)))
|
||||
c.Assert(string(out), check.Equals, "")
|
||||
}
|
||||
|
||||
// startRegistry starts the OpenShift registry with configPart on port, waits for it to be ready, and returns the process object, or terminates on failure.
|
||||
func (cluster *openshiftCluster) startRegistryProcess(t *testing.T, port uint16, configPath string) *exec.Cmd {
|
||||
func (cluster *openshiftCluster) startRegistryProcess(c *check.C, port int, configPath string) *exec.Cmd {
|
||||
cmd := cluster.clusterCmd(map[string]string{
|
||||
"KUBECONFIG": "openshift.local.registry/openshift-registry.kubeconfig",
|
||||
"DOCKER_REGISTRY_URL": fmt.Sprintf("127.0.0.1:%d", port),
|
||||
}, "dockerregistry", configPath)
|
||||
consumeAndLogOutputs(t, fmt.Sprintf("registry-%d", port), cmd)
|
||||
consumeAndLogOutputs(c, fmt.Sprintf("registry-%d", port), cmd)
|
||||
err := cmd.Start()
|
||||
require.NoError(t, err, "%s")
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
portOpen, terminatePortCheck := newPortChecker(t, port)
|
||||
portOpen, terminatePortCheck := newPortChecker(c, port)
|
||||
defer func() {
|
||||
terminatePortCheck <- true
|
||||
}()
|
||||
t.Logf("Waiting for registry to start")
|
||||
c.Logf("Waiting for registry to start")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
|
||||
defer cancel()
|
||||
select {
|
||||
case <-portOpen:
|
||||
t.Logf("OK, Registry port open")
|
||||
c.Logf("OK, Registry port open")
|
||||
case <-ctx.Done():
|
||||
t.Fatalf("Timed out waiting for registry to start: %v", ctx.Err())
|
||||
c.Fatalf("Timed out waiting for registry to start: %v", ctx.Err())
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// startRegistry starts the OpenShift registry and waits for it to be ready, or terminates on failure.
|
||||
func (cluster *openshiftCluster) startRegistry(t *testing.T) {
|
||||
func (cluster *openshiftCluster) startRegistry(c *check.C) {
|
||||
// Our “primary” registry
|
||||
cluster.processes = append(cluster.processes, cluster.startRegistryProcess(t, 5000, "/atomic-registry-config.yml"))
|
||||
cluster.processes = append(cluster.processes, cluster.startRegistryProcess(c, 5000, "/atomic-registry-config.yml"))
|
||||
|
||||
// A registry configured with acceptschema2:false
|
||||
schema1Config := fileFromFixture(t, "/atomic-registry-config.yml", map[string]string{
|
||||
schema1Config := fileFromFixture(c, "/atomic-registry-config.yml", map[string]string{
|
||||
"addr: :5000": "addr: :5005",
|
||||
"rootdirectory: /registry": "rootdirectory: /registry-schema1",
|
||||
// The default configuration currently already contains acceptschema2: false
|
||||
})
|
||||
// Make sure the configuration contains "acceptschema2: false", because eventually it will be enabled upstream and this function will need to be updated.
|
||||
configContents, err := os.ReadFile(schema1Config)
|
||||
require.NoError(t, err)
|
||||
require.Regexp(t, "(?s).*acceptschema2: false.*", string(configContents))
|
||||
cluster.processes = append(cluster.processes, cluster.startRegistryProcess(t, 5005, schema1Config))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(string(configContents), check.Matches, "(?s).*acceptschema2: false.*")
|
||||
cluster.processes = append(cluster.processes, cluster.startRegistryProcess(c, 5005, schema1Config))
|
||||
|
||||
// A registry configured with acceptschema2:true
|
||||
schema2Config := fileFromFixture(t, "/atomic-registry-config.yml", map[string]string{
|
||||
schema2Config := fileFromFixture(c, "/atomic-registry-config.yml", map[string]string{
|
||||
"addr: :5000": "addr: :5006",
|
||||
"rootdirectory: /registry": "rootdirectory: /registry-schema2",
|
||||
"acceptschema2: false": "acceptschema2: true",
|
||||
})
|
||||
cluster.processes = append(cluster.processes, cluster.startRegistryProcess(t, 5006, schema2Config))
|
||||
cluster.processes = append(cluster.processes, cluster.startRegistryProcess(c, 5006, schema2Config))
|
||||
}
|
||||
|
||||
// ocLogin runs (oc login) and (oc new-project) on the cluster, or terminates on failure.
|
||||
func (cluster *openshiftCluster) ocLoginToProject(t *testing.T) {
|
||||
t.Logf("oc login")
|
||||
func (cluster *openshiftCluster) ocLoginToProject(c *check.C) {
|
||||
c.Logf("oc login")
|
||||
cmd := cluster.clusterCmd(nil, "oc", "login", "--certificate-authority=openshift.local.config/master/ca.crt", "-u", "myuser", "-p", "mypw", "https://localhost:8443")
|
||||
out, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, "%s", out)
|
||||
require.Regexp(t, "(?s).*Login successful.*", string(out)) // (?s) : '.' will also match newlines
|
||||
c.Assert(err, check.IsNil, check.Commentf("%s", out))
|
||||
c.Assert(string(out), check.Matches, "(?s).*Login successful.*") // (?s) : '.' will also match newlines
|
||||
|
||||
outString := combinedOutputOfCommand(t, "oc", "new-project", "myns")
|
||||
require.Regexp(t, `(?s).*Now using project "myns".*`, outString) // (?s) : '.' will also match newlines
|
||||
outString := combinedOutputOfCommand(c, "oc", "new-project", "myns")
|
||||
c.Assert(outString, check.Matches, `(?s).*Now using project "myns".*`) // (?s) : '.' will also match newlines
|
||||
}
|
||||
|
||||
// dockerLogin simulates (docker login) to the cluster, or terminates on failure.
|
||||
// We do not run (docker login) directly, because that requires a running daemon and a docker package.
|
||||
func (cluster *openshiftCluster) dockerLogin(t *testing.T) {
|
||||
func (cluster *openshiftCluster) dockerLogin(c *check.C) {
|
||||
cluster.dockerDir = filepath.Join(homedir.Get(), ".docker")
|
||||
err := os.Mkdir(cluster.dockerDir, 0700)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
out := combinedOutputOfCommand(t, "oc", "config", "view", "-o", "json", "-o", "jsonpath={.users[*].user.token}")
|
||||
t.Logf("oc config value: %s", out)
|
||||
out := combinedOutputOfCommand(c, "oc", "config", "view", "-o", "json", "-o", "jsonpath={.users[*].user.token}")
|
||||
c.Logf("oc config value: %s", out)
|
||||
authValue := base64.StdEncoding.EncodeToString([]byte("unused:" + out))
|
||||
auths := []string{}
|
||||
for _, port := range []int{5000, 5005, 5006} {
|
||||
@@ -237,22 +237,22 @@ func (cluster *openshiftCluster) dockerLogin(t *testing.T) {
|
||||
}
|
||||
configJSON := `{"auths": {` + strings.Join(auths, ",") + `}}`
|
||||
err = os.WriteFile(filepath.Join(cluster.dockerDir, "config.json"), []byte(configJSON), 0600)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
// relaxImageSignerPermissions opens up the system:image-signer permissions so that
|
||||
// anyone can work with signatures
|
||||
// FIXME: This also allows anyone to DoS anyone else; this design is really not all
|
||||
// that workable, but it is the best we can do for now.
|
||||
func (cluster *openshiftCluster) relaxImageSignerPermissions(t *testing.T) {
|
||||
func (cluster *openshiftCluster) relaxImageSignerPermissions(c *check.C) {
|
||||
cmd := cluster.clusterCmd(adminKUBECONFIG, "oadm", "policy", "add-cluster-role-to-group", "system:image-signer", "system:authenticated")
|
||||
out, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, "%s", string(out))
|
||||
require.Equal(t, "cluster role \"system:image-signer\" added: \"system:authenticated\"\n", string(out))
|
||||
c.Assert(err, check.IsNil, check.Commentf("%s", string(out)))
|
||||
c.Assert(string(out), check.Equals, "cluster role \"system:image-signer\" added: \"system:authenticated\"\n")
|
||||
}
|
||||
|
||||
// tearDown stops the cluster services and deletes (only some!) of the state.
|
||||
func (cluster *openshiftCluster) tearDown(t *testing.T) {
|
||||
func (cluster *openshiftCluster) tearDown(c *check.C) {
|
||||
for i := len(cluster.processes) - 1; i >= 0; i-- {
|
||||
// It’s undocumented what Kill() returns if the process has terminated,
|
||||
// so we couldn’t check just for that. This is running in a container anyway…
|
||||
@@ -260,6 +260,6 @@ func (cluster *openshiftCluster) tearDown(t *testing.T) {
|
||||
}
|
||||
if cluster.dockerDir != "" {
|
||||
err := os.RemoveAll(cluster.dockerDir)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -21,7 +20,7 @@ To use it, run:
|
||||
|
||||
to start a container, then within the container:
|
||||
|
||||
SKOPEO_CONTAINER_TESTS=1 PS1='nested> ' go test -tags openshift_shell -timeout=24h ./integration -v -run='copySuite.TestRunShell'
|
||||
SKOPEO_CONTAINER_TESTS=1 PS1='nested> ' go test -tags openshift_shell -timeout=24h ./integration -v -check.v -check.vv -check.f='CopySuite.TestRunShell'
|
||||
|
||||
An example of what can be done within the container:
|
||||
|
||||
@@ -34,14 +33,13 @@ An example of what can be done within the container:
|
||||
curl -L -v 'http://localhost:5000/v2/myns/personal/manifests/personal' --header 'Authorization: Bearer $token_from_oauth'
|
||||
curl -L -v 'http://localhost:5000/extensions/v2/myns/personal/signatures/$manifest_digest' --header 'Authorization: Bearer $token_from_oauth'
|
||||
*/
|
||||
func (s *copySuite) TestRunShell() {
|
||||
t := s.T()
|
||||
func (s *CopySuite) TestRunShell(c *check.C) {
|
||||
cmd := exec.Command("bash", "-i")
|
||||
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
cmd.Stdin = tty
|
||||
cmd.Stdout = tty
|
||||
cmd.Stderr = tty
|
||||
err = cmd.Run()
|
||||
assert.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@ import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// cmdLifecycleToParentIfPossible tries to exit if the parent process exits (only works on Linux).
|
||||
// cmdLifecycleToParentIfPossible tries to exit if the parent process exits (only works on Linux)
|
||||
func cmdLifecycleToParentIfPossible(c *exec.Cmd) {
|
||||
}
|
||||
|
||||
@@ -9,18 +9,16 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopkg.in/check.v1"
|
||||
|
||||
"github.com/containers/image/v5/manifest"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
// This image is known to be x86_64 only right now
|
||||
const knownNotManifestListedImageX8664 = "docker://quay.io/coreos/11bot"
|
||||
const knownNotManifestListedImage_x8664 = "docker://quay.io/coreos/11bot"
|
||||
|
||||
// knownNotExtantImage would be very surprising if it did exist
|
||||
const knownNotExtantImage = "docker://quay.io/centos/centos:opensusewindowsubuntu"
|
||||
@@ -34,7 +32,7 @@ type request struct {
|
||||
// Method is the name of the function
|
||||
Method string `json:"method"`
|
||||
// Args is the arguments (parsed inside the function)
|
||||
Args []any `json:"args"`
|
||||
Args []interface{} `json:"args"`
|
||||
}
|
||||
|
||||
// reply is copied from proxy.go
|
||||
@@ -42,7 +40,7 @@ type reply struct {
|
||||
// Success is true if and only if the call succeeded.
|
||||
Success bool `json:"success"`
|
||||
// Value is an arbitrary value (or values, as array/map) returned from the call.
|
||||
Value any `json:"value"`
|
||||
Value interface{} `json:"value"`
|
||||
// PipeID is an index into open pipes, and should be passed to FinishPipe
|
||||
PipeID uint32 `json:"pipeid"`
|
||||
// Error should be non-empty if Success == false
|
||||
@@ -62,7 +60,7 @@ type pipefd struct {
|
||||
fd *os.File
|
||||
}
|
||||
|
||||
func (p *proxy) call(method string, args []any) (rval any, fd *pipefd, err error) {
|
||||
func (p *proxy) call(method string, args []interface{}) (rval interface{}, fd *pipefd, err error) {
|
||||
req := request{
|
||||
Method: method,
|
||||
Args: args,
|
||||
@@ -83,7 +81,7 @@ func (p *proxy) call(method string, args []any) (rval any, fd *pipefd, err error
|
||||
replybuf := make([]byte, maxMsgSize)
|
||||
n, oobn, _, _, err := p.c.ReadMsgUnix(replybuf, oob)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("reading reply: %w", err)
|
||||
err = fmt.Errorf("reading reply: %v", err)
|
||||
return
|
||||
}
|
||||
var reply reply
|
||||
@@ -101,7 +99,7 @@ func (p *proxy) call(method string, args []any) (rval any, fd *pipefd, err error
|
||||
var scms []syscall.SocketControlMessage
|
||||
scms, err = syscall.ParseSocketControlMessage(oob[:oobn])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to parse control message: %w", err)
|
||||
err = fmt.Errorf("failed to parse control message: %v", err)
|
||||
return
|
||||
}
|
||||
if len(scms) != 1 {
|
||||
@@ -111,7 +109,7 @@ func (p *proxy) call(method string, args []any) (rval any, fd *pipefd, err error
|
||||
var fds []int
|
||||
fds, err = syscall.ParseUnixRights(&scms[0])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to parse unix rights: %w", err)
|
||||
err = fmt.Errorf("failed to parse unix rights: %v", err)
|
||||
return
|
||||
}
|
||||
fd = &pipefd{
|
||||
@@ -124,7 +122,7 @@ func (p *proxy) call(method string, args []any) (rval any, fd *pipefd, err error
|
||||
return
|
||||
}
|
||||
|
||||
func (p *proxy) callNoFd(method string, args []any) (rval any, err error) {
|
||||
func (p *proxy) callNoFd(method string, args []interface{}) (rval interface{}, err error) {
|
||||
var fd *pipefd
|
||||
rval, fd, err = p.call(method, args)
|
||||
if err != nil {
|
||||
@@ -137,7 +135,7 @@ func (p *proxy) callNoFd(method string, args []any) (rval any, err error) {
|
||||
return rval, nil
|
||||
}
|
||||
|
||||
func (p *proxy) callReadAllBytes(method string, args []any) (rval any, buf []byte, err error) {
|
||||
func (p *proxy) callReadAllBytes(method string, args []interface{}) (rval interface{}, buf []byte, err error) {
|
||||
var fd *pipefd
|
||||
rval, fd, err = p.call(method, args)
|
||||
if err != nil {
|
||||
@@ -155,7 +153,7 @@ func (p *proxy) callReadAllBytes(method string, args []any) (rval any, buf []byt
|
||||
err: err,
|
||||
}
|
||||
}()
|
||||
_, err = p.callNoFd("FinishPipe", []any{fd.id})
|
||||
_, err = p.callNoFd("FinishPipe", []interface{}{fd.id})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -216,12 +214,17 @@ func newProxy() (*proxy, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func TestProxy(t *testing.T) {
|
||||
suite.Run(t, &proxySuite{})
|
||||
func init() {
|
||||
check.Suite(&ProxySuite{})
|
||||
}
|
||||
|
||||
type proxySuite struct {
|
||||
suite.Suite
|
||||
type ProxySuite struct {
|
||||
}
|
||||
|
||||
func (s *ProxySuite) SetUpSuite(c *check.C) {
|
||||
}
|
||||
|
||||
func (s *ProxySuite) TearDownSuite(c *check.C) {
|
||||
}
|
||||
|
||||
type byteFetch struct {
|
||||
@@ -230,7 +233,7 @@ type byteFetch struct {
|
||||
}
|
||||
|
||||
func runTestGetManifestAndConfig(p *proxy, img string) error {
|
||||
v, err := p.callNoFd("OpenImage", []any{img})
|
||||
v, err := p.callNoFd("OpenImage", []interface{}{knownNotManifestListedImage_x8664})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -245,7 +248,7 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
||||
}
|
||||
|
||||
// Also verify the optional path
|
||||
v, err = p.callNoFd("OpenImageOptional", []any{img})
|
||||
v, err = p.callNoFd("OpenImageOptional", []interface{}{knownNotManifestListedImage_x8664})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -259,12 +262,12 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
||||
return fmt.Errorf("got zero from expected image")
|
||||
}
|
||||
|
||||
_, err = p.callNoFd("CloseImage", []any{imgid2})
|
||||
_, err = p.callNoFd("CloseImage", []interface{}{imgid2})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, manifestBytes, err := p.callReadAllBytes("GetManifest", []any{imgid})
|
||||
_, manifestBytes, err := p.callReadAllBytes("GetManifest", []interface{}{imgid})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -273,7 +276,7 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, configBytes, err := p.callReadAllBytes("GetFullConfig", []any{imgid})
|
||||
_, configBytes, err := p.callReadAllBytes("GetFullConfig", []interface{}{imgid})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -292,7 +295,7 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
||||
}
|
||||
|
||||
// Also test this legacy interface
|
||||
_, ctrconfigBytes, err := p.callReadAllBytes("GetConfig", []any{imgid})
|
||||
_, ctrconfigBytes, err := p.callReadAllBytes("GetConfig", []interface{}{imgid})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -307,7 +310,7 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
||||
return fmt.Errorf("No CMD or ENTRYPOINT set")
|
||||
}
|
||||
|
||||
_, err = p.callNoFd("CloseImage", []any{imgid})
|
||||
_, err = p.callNoFd("CloseImage", []interface{}{imgid})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -316,7 +319,7 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
||||
}
|
||||
|
||||
func runTestOpenImageOptionalNotFound(p *proxy, img string) error {
|
||||
v, err := p.callNoFd("OpenImageOptional", []any{img})
|
||||
v, err := p.callNoFd("OpenImageOptional", []interface{}{img})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -332,26 +335,25 @@ func runTestOpenImageOptionalNotFound(p *proxy, img string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *proxySuite) TestProxy() {
|
||||
t := s.T()
|
||||
func (s *ProxySuite) TestProxy(c *check.C) {
|
||||
p, err := newProxy()
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
err = runTestGetManifestAndConfig(p, knownNotManifestListedImageX8664)
|
||||
err = runTestGetManifestAndConfig(p, knownNotManifestListedImage_x8664)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Testing image %s: %v", knownNotManifestListedImageX8664, err)
|
||||
err = fmt.Errorf("Testing image %s: %v", knownNotManifestListedImage_x8664, err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
err = runTestGetManifestAndConfig(p, knownListImage)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Testing image %s: %v", knownListImage, err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
err = runTestOpenImageOptionalNotFound(p, knownNotExtantImage)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Testing optional image %s: %v", knownNotExtantImage, err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -25,9 +24,9 @@ type testRegistryV2 struct {
|
||||
email string
|
||||
}
|
||||
|
||||
func setupRegistryV2At(t *testing.T, url string, auth, schema1 bool) *testRegistryV2 {
|
||||
reg, err := newTestRegistryV2At(t, url, auth, schema1)
|
||||
require.NoError(t, err)
|
||||
func setupRegistryV2At(c *check.C, url string, auth, schema1 bool) *testRegistryV2 {
|
||||
reg, err := newTestRegistryV2At(c, url, auth, schema1)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// Wait for registry to be ready to serve requests.
|
||||
for i := 0; i != 50; i++ {
|
||||
@@ -38,13 +37,13 @@ func setupRegistryV2At(t *testing.T, url string, auth, schema1 bool) *testRegist
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Timeout waiting for test registry to become available")
|
||||
c.Fatal("Timeout waiting for test registry to become available")
|
||||
}
|
||||
return reg
|
||||
}
|
||||
|
||||
func newTestRegistryV2At(t *testing.T, url string, auth, schema1 bool) (*testRegistryV2, error) {
|
||||
tmp := t.TempDir()
|
||||
func newTestRegistryV2At(c *check.C, url string, auth, schema1 bool) (*testRegistryV2, error) {
|
||||
tmp := c.MkDir()
|
||||
template := `version: 0.1
|
||||
loglevel: debug
|
||||
storage:
|
||||
@@ -95,10 +94,10 @@ compatibility:
|
||||
cmd = exec.Command(binaryV2, "serve", confPath)
|
||||
}
|
||||
|
||||
consumeAndLogOutputs(t, fmt.Sprintf("registry-%s", url), cmd)
|
||||
consumeAndLogOutputs(c, fmt.Sprintf("registry-%s", url), cmd)
|
||||
if err := cmd.Start(); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
t.Skip(err.Error())
|
||||
c.Skip(err.Error())
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@@ -111,9 +110,9 @@ compatibility:
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *testRegistryV2) Ping() error {
|
||||
func (t *testRegistryV2) Ping() error {
|
||||
// We always ping through HTTP for our test registry.
|
||||
resp, err := http.Get(fmt.Sprintf("http://%s/v2/", r.url))
|
||||
resp, err := http.Get(fmt.Sprintf("http://%s/v2/", t.url))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -124,8 +123,8 @@ func (r *testRegistryV2) Ping() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *testRegistryV2) tearDown() {
|
||||
func (t *testRegistryV2) tearDown(c *check.C) {
|
||||
// It’s undocumented what Kill() returns if the process has terminated,
|
||||
// so we couldn’t check just for that. This is running in a container anyway…
|
||||
_ = r.cmd.Process.Kill()
|
||||
_ = t.cmd.Process.Kill()
|
||||
}
|
||||
|
||||
@@ -6,28 +6,23 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/v5/signature"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
gpgBinary = "gpg"
|
||||
)
|
||||
|
||||
func TestSigning(t *testing.T) {
|
||||
suite.Run(t, &signingSuite{})
|
||||
func init() {
|
||||
check.Suite(&SigningSuite{})
|
||||
}
|
||||
|
||||
type signingSuite struct {
|
||||
suite.Suite
|
||||
type SigningSuite struct {
|
||||
fingerprint string
|
||||
}
|
||||
|
||||
var _ = suite.SetupAllSuite(&signingSuite{})
|
||||
|
||||
func findFingerprint(lineBytes []byte) (string, error) {
|
||||
lines := string(lineBytes)
|
||||
for _, line := range strings.Split(lines, "\n") {
|
||||
@@ -39,41 +34,43 @@ func findFingerprint(lineBytes []byte) (string, error) {
|
||||
return "", errors.New("No fingerprint found")
|
||||
}
|
||||
|
||||
func (s *signingSuite) SetupSuite() {
|
||||
t := s.T()
|
||||
func (s *SigningSuite) SetUpSuite(c *check.C) {
|
||||
_, err := exec.LookPath(skopeoBinary)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
gpgHome := t.TempDir()
|
||||
t.Setenv("GNUPGHOME", gpgHome)
|
||||
gpgHome := c.MkDir()
|
||||
os.Setenv("GNUPGHOME", gpgHome)
|
||||
|
||||
runCommandWithInput(t, "Key-Type: RSA\nName-Real: Testing user\n%no-protection\n%commit\n", gpgBinary, "--homedir", gpgHome, "--batch", "--gen-key")
|
||||
runCommandWithInput(c, "Key-Type: RSA\nName-Real: Testing user\n%no-protection\n%commit\n", gpgBinary, "--homedir", gpgHome, "--batch", "--gen-key")
|
||||
|
||||
lines, err := exec.Command(gpgBinary, "--homedir", gpgHome, "--with-colons", "--no-permission-warning", "--fingerprint").Output()
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
s.fingerprint, err = findFingerprint(lines)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
func (s *signingSuite) TestSignVerifySmoke() {
|
||||
t := s.T()
|
||||
func (s *SigningSuite) TearDownSuite(c *check.C) {
|
||||
os.Unsetenv("GNUPGHOME")
|
||||
}
|
||||
|
||||
func (s *SigningSuite) TestSignVerifySmoke(c *check.C) {
|
||||
mech, _, err := signature.NewEphemeralGPGSigningMechanism([]byte{})
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
defer mech.Close()
|
||||
if err := mech.SupportsSigning(); err != nil { // FIXME? Test that verification and policy enforcement works, using signatures from fixtures
|
||||
t.Skipf("Signing not supported: %v", err)
|
||||
c.Skip(fmt.Sprintf("Signing not supported: %v", err))
|
||||
}
|
||||
|
||||
manifestPath := "fixtures/image.manifest.json"
|
||||
dockerReference := "testing/smoketest"
|
||||
|
||||
sigOutput, err := os.CreateTemp("", "sig")
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
defer os.Remove(sigOutput.Name())
|
||||
assertSkopeoSucceeds(t, "^$", "standalone-sign", "-o", sigOutput.Name(),
|
||||
assertSkopeoSucceeds(c, "^$", "standalone-sign", "-o", sigOutput.Name(),
|
||||
manifestPath, dockerReference, s.fingerprint)
|
||||
|
||||
expected := fmt.Sprintf("^Signature verified using fingerprint %s, digest %s\n$", s.fingerprint, TestImageManifestDigest)
|
||||
assertSkopeoSucceeds(t, expected, "standalone-verify", manifestPath,
|
||||
expected := fmt.Sprintf("^Signature verified, digest %s\n$", TestImageManifestDigest)
|
||||
assertSkopeoSucceeds(c, expected, "standalone-verify", manifestPath,
|
||||
dockerReference, s.fingerprint, sigOutput.Name())
|
||||
}
|
||||
|
||||
@@ -9,63 +9,54 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/v5/docker"
|
||||
"github.com/containers/image/v5/docker/reference"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/containers/image/v5/types"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
// A repository with a path with multiple components in it which
|
||||
// contains multiple tags, preferably with some tags pointing to
|
||||
// manifest lists, and with some tags that don't.
|
||||
pullableRepo = "k8s.gcr.io/coredns/coredns"
|
||||
pullableRepo = "registry.k8s.io/coredns/coredns"
|
||||
// A tagged image in the repository that we can inspect and copy.
|
||||
pullableTaggedImage = "k8s.gcr.io/coredns/coredns:v1.6.6"
|
||||
pullableTaggedImage = "registry.k8s.io/coredns/coredns:v1.6.6"
|
||||
// A tagged manifest list in the repository that we can inspect and copy.
|
||||
pullableTaggedManifestList = "k8s.gcr.io/coredns/coredns:v1.8.0"
|
||||
pullableTaggedManifestList = "registry.k8s.io/coredns/coredns:v1.8.0"
|
||||
// A repository containing multiple tags, some of which are for
|
||||
// manifest lists, and which includes a "latest" tag. We specify the
|
||||
// name here without a tag.
|
||||
pullableRepoWithLatestTag = "k8s.gcr.io/pause"
|
||||
pullableRepoWithLatestTag = "registry.k8s.io/pause"
|
||||
)
|
||||
|
||||
func TestSync(t *testing.T) {
|
||||
suite.Run(t, &syncSuite{})
|
||||
func init() {
|
||||
check.Suite(&SyncSuite{})
|
||||
}
|
||||
|
||||
type syncSuite struct {
|
||||
suite.Suite
|
||||
type SyncSuite struct {
|
||||
cluster *openshiftCluster
|
||||
registry *testRegistryV2
|
||||
}
|
||||
|
||||
var _ = suite.SetupAllSuite(&syncSuite{})
|
||||
var _ = suite.TearDownAllSuite(&syncSuite{})
|
||||
|
||||
func (s *syncSuite) SetupSuite() {
|
||||
t := s.T()
|
||||
|
||||
func (s *SyncSuite) SetUpSuite(c *check.C) {
|
||||
const registryAuth = false
|
||||
const registrySchema1 = false
|
||||
|
||||
if os.Getenv("SKOPEO_LOCAL_TESTS") == "1" {
|
||||
t.Log("Running tests without a container")
|
||||
c.Log("Running tests without a container")
|
||||
fmt.Printf("NOTE: tests requires a V2 registry at url=%s, with auth=%t, schema1=%t \n", v2DockerRegistryURL, registryAuth, registrySchema1)
|
||||
return
|
||||
}
|
||||
|
||||
if os.Getenv("SKOPEO_CONTAINER_TESTS") != "1" {
|
||||
t.Skip("Not running in a container, refusing to affect user state")
|
||||
c.Skip("Not running in a container, refusing to affect user state")
|
||||
}
|
||||
|
||||
s.cluster = startOpenshiftCluster(t) // FIXME: Set up TLS for the docker registry port instead of using "--tls-verify=false" all over the place.
|
||||
s.cluster = startOpenshiftCluster(c) // FIXME: Set up TLS for the docker registry port instead of using "--tls-verify=false" all over the place.
|
||||
|
||||
for _, stream := range []string{"unsigned", "personal", "official", "naming", "cosigned", "compression", "schema1", "schema2"} {
|
||||
isJSON := fmt.Sprintf(`{
|
||||
@@ -76,42 +67,41 @@ func (s *syncSuite) SetupSuite() {
|
||||
},
|
||||
"spec": {}
|
||||
}`, stream)
|
||||
runCommandWithInput(t, isJSON, "oc", "create", "-f", "-")
|
||||
runCommandWithInput(c, isJSON, "oc", "create", "-f", "-")
|
||||
}
|
||||
|
||||
// FIXME: Set up TLS for the docker registry port instead of using "--tls-verify=false" all over the place.
|
||||
s.registry = setupRegistryV2At(t, v2DockerRegistryURL, registryAuth, registrySchema1)
|
||||
s.registry = setupRegistryV2At(c, v2DockerRegistryURL, registryAuth, registrySchema1)
|
||||
|
||||
gpgHome := t.TempDir()
|
||||
t.Setenv("GNUPGHOME", gpgHome)
|
||||
gpgHome := c.MkDir()
|
||||
os.Setenv("GNUPGHOME", gpgHome)
|
||||
|
||||
for _, key := range []string{"personal", "official"} {
|
||||
batchInput := fmt.Sprintf("Key-Type: RSA\nName-Real: Test key - %s\nName-email: %s@example.com\n%%no-protection\n%%commit\n",
|
||||
key, key)
|
||||
runCommandWithInput(t, batchInput, gpgBinary, "--batch", "--gen-key")
|
||||
runCommandWithInput(c, batchInput, gpgBinary, "--batch", "--gen-key")
|
||||
|
||||
out := combinedOutputOfCommand(t, gpgBinary, "--armor", "--export", fmt.Sprintf("%s@example.com", key))
|
||||
out := combinedOutputOfCommand(c, gpgBinary, "--armor", "--export", fmt.Sprintf("%s@example.com", key))
|
||||
err := os.WriteFile(filepath.Join(gpgHome, fmt.Sprintf("%s-pubkey.gpg", key)),
|
||||
[]byte(out), 0600)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *syncSuite) TearDownSuite() {
|
||||
t := s.T()
|
||||
func (s *SyncSuite) TearDownSuite(c *check.C) {
|
||||
if os.Getenv("SKOPEO_LOCAL_TESTS") == "1" {
|
||||
return
|
||||
}
|
||||
|
||||
if s.registry != nil {
|
||||
s.registry.tearDown()
|
||||
s.registry.tearDown(c)
|
||||
}
|
||||
if s.cluster != nil {
|
||||
s.cluster.tearDown(t)
|
||||
s.cluster.tearDown(c)
|
||||
}
|
||||
}
|
||||
|
||||
func assertNumberOfManifestsInSubdirs(t *testing.T, dir string, expectedCount int) {
|
||||
func assertNumberOfManifestsInSubdirs(c *check.C, dir string, expectedCount int) {
|
||||
nManifests := 0
|
||||
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
@@ -123,163 +113,156 @@ func assertNumberOfManifestsInSubdirs(t *testing.T, dir string, expectedCount in
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedCount, nManifests)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(nManifests, check.Equals, expectedCount)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestDocker2DirTagged() {
|
||||
t := s.T()
|
||||
tmpDir := t.TempDir()
|
||||
func (s *SyncSuite) TestDocker2DirTagged(c *check.C) {
|
||||
tmpDir := c.MkDir()
|
||||
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
image := pullableTaggedImage
|
||||
imageRef, err := docker.ParseReference(fmt.Sprintf("//%s", image))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
imagePath := imageRef.DockerReference().String()
|
||||
|
||||
dir1 := path.Join(tmpDir, "dir1")
|
||||
dir2 := path.Join(tmpDir, "dir2")
|
||||
|
||||
// sync docker => dir
|
||||
assertSkopeoSucceeds(t, "", "sync", "--scoped", "--src", "docker", "--dest", "dir", image, dir1)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "docker", "--dest", "dir", image, dir1)
|
||||
_, err = os.Stat(path.Join(dir1, imagePath, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// copy docker => dir
|
||||
assertSkopeoSucceeds(t, "", "copy", "docker://"+image, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(c, "", "copy", "docker://"+image, "dir:"+dir2)
|
||||
_, err = os.Stat(path.Join(dir2, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
out := combinedOutputOfCommand(t, "diff", "-urN", path.Join(dir1, imagePath), dir2)
|
||||
assert.Equal(t, "", out)
|
||||
out := combinedOutputOfCommand(c, "diff", "-urN", path.Join(dir1, imagePath), dir2)
|
||||
c.Assert(out, check.Equals, "")
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestDocker2DirTaggedAll() {
|
||||
t := s.T()
|
||||
tmpDir := t.TempDir()
|
||||
func (s *SyncSuite) TestDocker2DirTaggedAll(c *check.C) {
|
||||
tmpDir := c.MkDir()
|
||||
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
image := pullableTaggedManifestList
|
||||
imageRef, err := docker.ParseReference(fmt.Sprintf("//%s", image))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
imagePath := imageRef.DockerReference().String()
|
||||
|
||||
dir1 := path.Join(tmpDir, "dir1")
|
||||
dir2 := path.Join(tmpDir, "dir2")
|
||||
|
||||
// sync docker => dir
|
||||
assertSkopeoSucceeds(t, "", "sync", "--all", "--scoped", "--src", "docker", "--dest", "dir", image, dir1)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--all", "--scoped", "--src", "docker", "--dest", "dir", image, dir1)
|
||||
_, err = os.Stat(path.Join(dir1, imagePath, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// copy docker => dir
|
||||
assertSkopeoSucceeds(t, "", "copy", "--all", "docker://"+image, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(c, "", "copy", "--all", "docker://"+image, "dir:"+dir2)
|
||||
_, err = os.Stat(path.Join(dir2, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
out := combinedOutputOfCommand(t, "diff", "-urN", path.Join(dir1, imagePath), dir2)
|
||||
assert.Equal(t, "", out)
|
||||
out := combinedOutputOfCommand(c, "diff", "-urN", path.Join(dir1, imagePath), dir2)
|
||||
c.Assert(out, check.Equals, "")
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestPreserveDigests() {
|
||||
t := s.T()
|
||||
tmpDir := t.TempDir()
|
||||
func (s *SyncSuite) TestPreserveDigests(c *check.C) {
|
||||
tmpDir := c.MkDir()
|
||||
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
image := pullableTaggedManifestList
|
||||
|
||||
// copy docker => dir
|
||||
assertSkopeoSucceeds(t, "", "copy", "--all", "--preserve-digests", "docker://"+image, "dir:"+tmpDir)
|
||||
assertSkopeoSucceeds(c, "", "copy", "--all", "--preserve-digests", "docker://"+image, "dir:"+tmpDir)
|
||||
_, err := os.Stat(path.Join(tmpDir, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
assertSkopeoFails(t, ".*Instructed to preserve digests.*", "copy", "--all", "--preserve-digests", "--format=oci", "docker://"+image, "dir:"+tmpDir)
|
||||
assertSkopeoFails(c, ".*Instructed to preserve digests.*", "copy", "--all", "--preserve-digests", "--format=oci", "docker://"+image, "dir:"+tmpDir)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestScoped() {
|
||||
t := s.T()
|
||||
func (s *SyncSuite) TestScoped(c *check.C) {
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
image := pullableTaggedImage
|
||||
imageRef, err := docker.ParseReference(fmt.Sprintf("//%s", image))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
imagePath := imageRef.DockerReference().String()
|
||||
|
||||
dir1 := t.TempDir()
|
||||
assertSkopeoSucceeds(t, "", "sync", "--src", "docker", "--dest", "dir", image, dir1)
|
||||
dir1 := c.MkDir()
|
||||
assertSkopeoSucceeds(c, "", "sync", "--src", "docker", "--dest", "dir", image, dir1)
|
||||
_, err = os.Stat(path.Join(dir1, path.Base(imagePath), "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
assertSkopeoSucceeds(t, "", "sync", "--scoped", "--src", "docker", "--dest", "dir", image, dir1)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "docker", "--dest", "dir", image, dir1)
|
||||
_, err = os.Stat(path.Join(dir1, imagePath, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestDirIsNotOverwritten() {
|
||||
t := s.T()
|
||||
func (s *SyncSuite) TestDirIsNotOverwritten(c *check.C) {
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
image := pullableRepoWithLatestTag
|
||||
imageRef, err := docker.ParseReference(fmt.Sprintf("//%s", image))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
imagePath := imageRef.DockerReference().String()
|
||||
|
||||
// make a copy of the image in the local registry
|
||||
assertSkopeoSucceeds(t, "", "copy", "--dest-tls-verify=false", "docker://"+image, "docker://"+path.Join(v2DockerRegistryURL, reference.Path(imageRef.DockerReference())))
|
||||
assertSkopeoSucceeds(c, "", "copy", "--dest-tls-verify=false", "docker://"+image, "docker://"+path.Join(v2DockerRegistryURL, reference.Path(imageRef.DockerReference())))
|
||||
|
||||
//sync upstream image to dir, not scoped
|
||||
dir1 := t.TempDir()
|
||||
assertSkopeoSucceeds(t, "", "sync", "--src", "docker", "--dest", "dir", image, dir1)
|
||||
dir1 := c.MkDir()
|
||||
assertSkopeoSucceeds(c, "", "sync", "--src", "docker", "--dest", "dir", image, dir1)
|
||||
_, err = os.Stat(path.Join(dir1, path.Base(imagePath), "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
//sync local registry image to dir, not scoped
|
||||
assertSkopeoFails(t, ".*Refusing to overwrite destination directory.*", "sync", "--src-tls-verify=false", "--src", "docker", "--dest", "dir", path.Join(v2DockerRegistryURL, reference.Path(imageRef.DockerReference())), dir1)
|
||||
assertSkopeoFails(c, ".*Refusing to overwrite destination directory.*", "sync", "--src-tls-verify=false", "--src", "docker", "--dest", "dir", path.Join(v2DockerRegistryURL, reference.Path(imageRef.DockerReference())), dir1)
|
||||
|
||||
//sync local registry image to dir, scoped
|
||||
imageRef, err = docker.ParseReference(fmt.Sprintf("//%s", path.Join(v2DockerRegistryURL, reference.Path(imageRef.DockerReference()))))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
imagePath = imageRef.DockerReference().String()
|
||||
assertSkopeoSucceeds(t, "", "sync", "--scoped", "--src-tls-verify=false", "--src", "docker", "--dest", "dir", path.Join(v2DockerRegistryURL, reference.Path(imageRef.DockerReference())), dir1)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src-tls-verify=false", "--src", "docker", "--dest", "dir", path.Join(v2DockerRegistryURL, reference.Path(imageRef.DockerReference())), dir1)
|
||||
_, err = os.Stat(path.Join(dir1, imagePath, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestDocker2DirUntagged() {
|
||||
t := s.T()
|
||||
tmpDir := t.TempDir()
|
||||
func (s *SyncSuite) TestDocker2DirUntagged(c *check.C) {
|
||||
tmpDir := c.MkDir()
|
||||
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
image := pullableRepo
|
||||
imageRef, err := docker.ParseReference(fmt.Sprintf("//%s", image))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
imagePath := imageRef.DockerReference().String()
|
||||
|
||||
dir1 := path.Join(tmpDir, "dir1")
|
||||
assertSkopeoSucceeds(t, "", "sync", "--scoped", "--src", "docker", "--dest", "dir", image, dir1)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "docker", "--dest", "dir", image, dir1)
|
||||
|
||||
sysCtx := types.SystemContext{}
|
||||
tags, err := docker.GetRepositoryTags(context.Background(), &sysCtx, imageRef)
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, len(tags))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Check(len(tags), check.Not(check.Equals), 0)
|
||||
|
||||
nManifests, err := filepath.Glob(path.Join(dir1, path.Dir(imagePath), "*", "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, nManifests, len(tags))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(len(nManifests), check.Equals, len(tags))
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestYamlUntagged() {
|
||||
t := s.T()
|
||||
tmpDir := t.TempDir()
|
||||
func (s *SyncSuite) TestYamlUntagged(c *check.C) {
|
||||
tmpDir := c.MkDir()
|
||||
dir1 := path.Join(tmpDir, "dir1")
|
||||
|
||||
image := pullableRepo
|
||||
imageRef, err := docker.ParseReference(fmt.Sprintf("//%s", image))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
imagePath := imageRef.DockerReference().Name()
|
||||
|
||||
sysCtx := types.SystemContext{}
|
||||
tags, err := docker.GetRepositoryTags(context.Background(), &sysCtx, imageRef)
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, len(tags))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Check(len(tags), check.Not(check.Equals), 0)
|
||||
|
||||
yamlConfig := fmt.Sprintf(`
|
||||
%s:
|
||||
@@ -290,8 +273,8 @@ func (s *syncSuite) TestYamlUntagged() {
|
||||
// sync to the local registry
|
||||
yamlFile := path.Join(tmpDir, "registries.yaml")
|
||||
err = os.WriteFile(yamlFile, []byte(yamlConfig), 0644)
|
||||
require.NoError(t, err)
|
||||
assertSkopeoSucceeds(t, "", "sync", "--scoped", "--src", "yaml", "--dest", "docker", "--dest-tls-verify=false", yamlFile, v2DockerRegistryURL)
|
||||
c.Assert(err, check.IsNil)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "docker", "--dest-tls-verify=false", yamlFile, v2DockerRegistryURL)
|
||||
// sync back from local registry to a folder
|
||||
os.Remove(yamlFile)
|
||||
yamlConfig = fmt.Sprintf(`
|
||||
@@ -302,67 +285,64 @@ func (s *syncSuite) TestYamlUntagged() {
|
||||
`, v2DockerRegistryURL, imagePath)
|
||||
|
||||
err = os.WriteFile(yamlFile, []byte(yamlConfig), 0644)
|
||||
require.NoError(t, err)
|
||||
assertSkopeoSucceeds(t, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
|
||||
c.Assert(err, check.IsNil)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
|
||||
|
||||
sysCtx = types.SystemContext{
|
||||
DockerInsecureSkipTLSVerify: types.NewOptionalBool(true),
|
||||
}
|
||||
localImageRef, err := docker.ParseReference(fmt.Sprintf("//%s/%s", v2DockerRegistryURL, imagePath))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
localTags, err := docker.GetRepositoryTags(context.Background(), &sysCtx, localImageRef)
|
||||
require.NoError(t, err)
|
||||
assert.NotZero(t, len(localTags))
|
||||
assert.Len(t, localTags, len(tags))
|
||||
assertNumberOfManifestsInSubdirs(t, dir1, len(tags))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Check(len(localTags), check.Not(check.Equals), 0)
|
||||
c.Assert(len(localTags), check.Equals, len(tags))
|
||||
assertNumberOfManifestsInSubdirs(c, dir1, len(tags))
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestYamlRegex2Dir() {
|
||||
t := s.T()
|
||||
tmpDir := t.TempDir()
|
||||
func (s *SyncSuite) TestYamlRegex2Dir(c *check.C) {
|
||||
tmpDir := c.MkDir()
|
||||
dir1 := path.Join(tmpDir, "dir1")
|
||||
|
||||
yamlConfig := `
|
||||
k8s.gcr.io:
|
||||
registry.k8s.io:
|
||||
images-by-tag-regex:
|
||||
pause: ^[12]\.0$ # regex string test
|
||||
`
|
||||
// the ↑ regex strings always matches only 2 images
|
||||
var nTags = 2
|
||||
assert.NotZero(t, nTags)
|
||||
c.Assert(nTags, check.Not(check.Equals), 0)
|
||||
|
||||
yamlFile := path.Join(tmpDir, "registries.yaml")
|
||||
err := os.WriteFile(yamlFile, []byte(yamlConfig), 0644)
|
||||
require.NoError(t, err)
|
||||
assertSkopeoSucceeds(t, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
|
||||
assertNumberOfManifestsInSubdirs(t, dir1, nTags)
|
||||
c.Assert(err, check.IsNil)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
|
||||
assertNumberOfManifestsInSubdirs(c, dir1, nTags)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestYamlDigest2Dir() {
|
||||
t := s.T()
|
||||
tmpDir := t.TempDir()
|
||||
func (s *SyncSuite) TestYamlDigest2Dir(c *check.C) {
|
||||
tmpDir := c.MkDir()
|
||||
dir1 := path.Join(tmpDir, "dir1")
|
||||
|
||||
yamlConfig := `
|
||||
k8s.gcr.io:
|
||||
registry.k8s.io:
|
||||
images:
|
||||
pause:
|
||||
- sha256:59eec8837a4d942cc19a52b8c09ea75121acc38114a2c68b98983ce9356b8610
|
||||
`
|
||||
yamlFile := path.Join(tmpDir, "registries.yaml")
|
||||
err := os.WriteFile(yamlFile, []byte(yamlConfig), 0644)
|
||||
require.NoError(t, err)
|
||||
assertSkopeoSucceeds(t, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
|
||||
assertNumberOfManifestsInSubdirs(t, dir1, 1)
|
||||
c.Assert(err, check.IsNil)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
|
||||
assertNumberOfManifestsInSubdirs(c, dir1, 1)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestYaml2Dir() {
|
||||
t := s.T()
|
||||
tmpDir := t.TempDir()
|
||||
func (s *SyncSuite) TestYaml2Dir(c *check.C) {
|
||||
tmpDir := c.MkDir()
|
||||
dir1 := path.Join(tmpDir, "dir1")
|
||||
|
||||
yamlConfig := `
|
||||
k8s.gcr.io:
|
||||
registry.k8s.io:
|
||||
images:
|
||||
coredns/coredns:
|
||||
- v1.8.0
|
||||
@@ -386,26 +366,25 @@ quay.io:
|
||||
nTags++
|
||||
}
|
||||
}
|
||||
assert.NotZero(t, nTags)
|
||||
c.Assert(nTags, check.Not(check.Equals), 0)
|
||||
|
||||
yamlFile := path.Join(tmpDir, "registries.yaml")
|
||||
err := os.WriteFile(yamlFile, []byte(yamlConfig), 0644)
|
||||
require.NoError(t, err)
|
||||
assertSkopeoSucceeds(t, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
|
||||
assertNumberOfManifestsInSubdirs(t, dir1, nTags)
|
||||
c.Assert(err, check.IsNil)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
|
||||
assertNumberOfManifestsInSubdirs(c, dir1, nTags)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestYamlTLSVerify() {
|
||||
t := s.T()
|
||||
func (s *SyncSuite) TestYamlTLSVerify(c *check.C) {
|
||||
const localRegURL = "docker://" + v2DockerRegistryURL + "/"
|
||||
tmpDir := t.TempDir()
|
||||
tmpDir := c.MkDir()
|
||||
dir1 := path.Join(tmpDir, "dir1")
|
||||
image := pullableRepoWithLatestTag
|
||||
tag := "latest"
|
||||
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
// copy docker => docker
|
||||
assertSkopeoSucceeds(t, "", "copy", "--dest-tls-verify=false", "docker://"+image+":"+tag, localRegURL+image+":"+tag)
|
||||
assertSkopeoSucceeds(c, "", "copy", "--dest-tls-verify=false", "docker://"+image+":"+tag, localRegURL+image+":"+tag)
|
||||
|
||||
yamlTemplate := `
|
||||
%s:
|
||||
@@ -417,7 +396,7 @@ func (s *syncSuite) TestYamlTLSVerify() {
|
||||
testCfg := []struct {
|
||||
tlsVerify string
|
||||
msg string
|
||||
checker func(t *testing.T, regexp string, args ...string)
|
||||
checker func(c *check.C, regexp string, args ...string)
|
||||
}{
|
||||
{
|
||||
tlsVerify: "tls-verify: false",
|
||||
@@ -441,18 +420,17 @@ func (s *syncSuite) TestYamlTLSVerify() {
|
||||
yamlConfig := fmt.Sprintf(yamlTemplate, v2DockerRegistryURL, cfg.tlsVerify, image, tag)
|
||||
yamlFile := path.Join(tmpDir, "registries.yaml")
|
||||
err := os.WriteFile(yamlFile, []byte(yamlConfig), 0644)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
cfg.checker(t, cfg.msg, "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
|
||||
cfg.checker(c, cfg.msg, "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
|
||||
os.Remove(yamlFile)
|
||||
os.RemoveAll(dir1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestSyncManifestOutput() {
|
||||
t := s.T()
|
||||
tmpDir := t.TempDir()
|
||||
func (s *SyncSuite) TestSyncManifestOutput(c *check.C) {
|
||||
tmpDir := c.MkDir()
|
||||
|
||||
destDir1 := filepath.Join(tmpDir, "dest1")
|
||||
destDir2 := filepath.Join(tmpDir, "dest2")
|
||||
@@ -461,162 +439,154 @@ func (s *syncSuite) TestSyncManifestOutput() {
|
||||
//Split image:tag path from image URI for manifest comparison
|
||||
imageDir := pullableTaggedImage[strings.LastIndex(pullableTaggedImage, "/")+1:]
|
||||
|
||||
assertSkopeoSucceeds(t, "", "sync", "--format=oci", "--all", "--src", "docker", "--dest", "dir", pullableTaggedImage, destDir1)
|
||||
verifyManifestMIMEType(t, filepath.Join(destDir1, imageDir), imgspecv1.MediaTypeImageManifest)
|
||||
assertSkopeoSucceeds(t, "", "sync", "--format=v2s2", "--all", "--src", "docker", "--dest", "dir", pullableTaggedImage, destDir2)
|
||||
verifyManifestMIMEType(t, filepath.Join(destDir2, imageDir), manifest.DockerV2Schema2MediaType)
|
||||
assertSkopeoSucceeds(t, "", "sync", "--format=v2s1", "--all", "--src", "docker", "--dest", "dir", pullableTaggedImage, destDir3)
|
||||
verifyManifestMIMEType(t, filepath.Join(destDir3, imageDir), manifest.DockerV2Schema1SignedMediaType)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--format=oci", "--all", "--src", "docker", "--dest", "dir", pullableTaggedImage, destDir1)
|
||||
verifyManifestMIMEType(c, filepath.Join(destDir1, imageDir), imgspecv1.MediaTypeImageManifest)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--format=v2s2", "--all", "--src", "docker", "--dest", "dir", pullableTaggedImage, destDir2)
|
||||
verifyManifestMIMEType(c, filepath.Join(destDir2, imageDir), manifest.DockerV2Schema2MediaType)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--format=v2s1", "--all", "--src", "docker", "--dest", "dir", pullableTaggedImage, destDir3)
|
||||
verifyManifestMIMEType(c, filepath.Join(destDir3, imageDir), manifest.DockerV2Schema1SignedMediaType)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestDocker2DockerTagged() {
|
||||
t := s.T()
|
||||
func (s *SyncSuite) TestDocker2DockerTagged(c *check.C) {
|
||||
const localRegURL = "docker://" + v2DockerRegistryURL + "/"
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
tmpDir := c.MkDir()
|
||||
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
image := pullableTaggedImage
|
||||
imageRef, err := docker.ParseReference(fmt.Sprintf("//%s", image))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
imagePath := imageRef.DockerReference().String()
|
||||
|
||||
dir1 := path.Join(tmpDir, "dir1")
|
||||
dir2 := path.Join(tmpDir, "dir2")
|
||||
|
||||
// sync docker => docker
|
||||
assertSkopeoSucceeds(t, "", "sync", "--scoped", "--dest-tls-verify=false", "--src", "docker", "--dest", "docker", image, v2DockerRegistryURL)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--dest-tls-verify=false", "--src", "docker", "--dest", "docker", image, v2DockerRegistryURL)
|
||||
|
||||
// copy docker => dir
|
||||
assertSkopeoSucceeds(t, "", "copy", "docker://"+image, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(c, "", "copy", "docker://"+image, "dir:"+dir1)
|
||||
_, err = os.Stat(path.Join(dir1, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// copy docker => dir
|
||||
assertSkopeoSucceeds(t, "", "copy", "--src-tls-verify=false", localRegURL+imagePath, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(c, "", "copy", "--src-tls-verify=false", localRegURL+imagePath, "dir:"+dir2)
|
||||
_, err = os.Stat(path.Join(dir2, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
out := combinedOutputOfCommand(t, "diff", "-urN", dir1, dir2)
|
||||
assert.Equal(t, "", out)
|
||||
out := combinedOutputOfCommand(c, "diff", "-urN", dir1, dir2)
|
||||
c.Assert(out, check.Equals, "")
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestDir2DockerTagged() {
|
||||
t := s.T()
|
||||
func (s *SyncSuite) TestDir2DockerTagged(c *check.C) {
|
||||
const localRegURL = "docker://" + v2DockerRegistryURL + "/"
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
tmpDir := c.MkDir()
|
||||
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
image := pullableRepoWithLatestTag
|
||||
|
||||
dir1 := path.Join(tmpDir, "dir1")
|
||||
err := os.Mkdir(dir1, 0755)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
dir2 := path.Join(tmpDir, "dir2")
|
||||
err = os.Mkdir(dir2, 0755)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// create leading dirs
|
||||
err = os.MkdirAll(path.Dir(path.Join(dir1, image)), 0755)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// copy docker => dir
|
||||
assertSkopeoSucceeds(t, "", "copy", "docker://"+image, "dir:"+path.Join(dir1, image))
|
||||
assertSkopeoSucceeds(c, "", "copy", "docker://"+image, "dir:"+path.Join(dir1, image))
|
||||
_, err = os.Stat(path.Join(dir1, image, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// sync dir => docker
|
||||
assertSkopeoSucceeds(t, "", "sync", "--scoped", "--dest-tls-verify=false", "--src", "dir", "--dest", "docker", dir1, v2DockerRegistryURL)
|
||||
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--dest-tls-verify=false", "--src", "dir", "--dest", "docker", dir1, v2DockerRegistryURL)
|
||||
|
||||
// create leading dirs
|
||||
err = os.MkdirAll(path.Dir(path.Join(dir2, image)), 0755)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// copy docker => dir
|
||||
assertSkopeoSucceeds(t, "", "copy", "--src-tls-verify=false", localRegURL+image, "dir:"+path.Join(dir2, image))
|
||||
assertSkopeoSucceeds(c, "", "copy", "--src-tls-verify=false", localRegURL+image, "dir:"+path.Join(dir2, image))
|
||||
_, err = os.Stat(path.Join(dir2, image, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
out := combinedOutputOfCommand(t, "diff", "-urN", dir1, dir2)
|
||||
assert.Equal(t, "", out)
|
||||
out := combinedOutputOfCommand(c, "diff", "-urN", dir1, dir2)
|
||||
c.Assert(out, check.Equals, "")
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestFailsWithDir2Dir() {
|
||||
t := s.T()
|
||||
tmpDir := t.TempDir()
|
||||
func (s *SyncSuite) TestFailsWithDir2Dir(c *check.C) {
|
||||
tmpDir := c.MkDir()
|
||||
|
||||
dir1 := path.Join(tmpDir, "dir1")
|
||||
dir2 := path.Join(tmpDir, "dir2")
|
||||
|
||||
// sync dir => dir is not allowed
|
||||
assertSkopeoFails(t, ".*sync from 'dir' to 'dir' not implemented.*", "sync", "--scoped", "--src", "dir", "--dest", "dir", dir1, dir2)
|
||||
assertSkopeoFails(c, ".*sync from 'dir' to 'dir' not implemented.*", "sync", "--scoped", "--src", "dir", "--dest", "dir", dir1, dir2)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestFailsNoSourceImages() {
|
||||
t := s.T()
|
||||
tmpDir := t.TempDir()
|
||||
func (s *SyncSuite) TestFailsNoSourceImages(c *check.C) {
|
||||
tmpDir := c.MkDir()
|
||||
|
||||
assertSkopeoFails(t, ".*No images to sync found in .*",
|
||||
assertSkopeoFails(c, ".*No images to sync found in .*",
|
||||
"sync", "--scoped", "--dest-tls-verify=false", "--src", "dir", "--dest", "docker", tmpDir, v2DockerRegistryURL)
|
||||
|
||||
assertSkopeoFails(t, ".*Error determining repository tags for repo docker.io/library/hopefully_no_images_will_ever_be_called_like_this: fetching tags list: requested access to the resource is denied.*",
|
||||
assertSkopeoFails(c, ".*Error determining repository tags for repo docker.io/library/hopefully_no_images_will_ever_be_called_like_this: fetching tags list: requested access to the resource is denied.*",
|
||||
"sync", "--scoped", "--dest-tls-verify=false", "--src", "docker", "--dest", "docker", "hopefully_no_images_will_ever_be_called_like_this", v2DockerRegistryURL)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestFailsWithDockerSourceNoRegistry() {
|
||||
t := s.T()
|
||||
func (s *SyncSuite) TestFailsWithDockerSourceNoRegistry(c *check.C) {
|
||||
const regURL = "google.com/namespace/imagename"
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
tmpDir := c.MkDir()
|
||||
|
||||
//untagged
|
||||
assertSkopeoFails(t, ".*StatusCode: 404.*",
|
||||
assertSkopeoFails(c, ".*StatusCode: 404.*",
|
||||
"sync", "--scoped", "--src", "docker", "--dest", "dir", regURL, tmpDir)
|
||||
|
||||
//tagged
|
||||
assertSkopeoFails(t, ".*StatusCode: 404.*",
|
||||
assertSkopeoFails(c, ".*StatusCode: 404.*",
|
||||
"sync", "--scoped", "--src", "docker", "--dest", "dir", regURL+":thetag", tmpDir)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestFailsWithDockerSourceUnauthorized() {
|
||||
t := s.T()
|
||||
func (s *SyncSuite) TestFailsWithDockerSourceUnauthorized(c *check.C) {
|
||||
const repo = "privateimagenamethatshouldnotbepublic"
|
||||
tmpDir := t.TempDir()
|
||||
tmpDir := c.MkDir()
|
||||
|
||||
//untagged
|
||||
assertSkopeoFails(t, ".*requested access to the resource is denied.*",
|
||||
assertSkopeoFails(c, ".*requested access to the resource is denied.*",
|
||||
"sync", "--scoped", "--src", "docker", "--dest", "dir", repo, tmpDir)
|
||||
|
||||
//tagged
|
||||
assertSkopeoFails(t, ".*requested access to the resource is denied.*",
|
||||
assertSkopeoFails(c, ".*requested access to the resource is denied.*",
|
||||
"sync", "--scoped", "--src", "docker", "--dest", "dir", repo+":thetag", tmpDir)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestFailsWithDockerSourceNotExisting() {
|
||||
t := s.T()
|
||||
func (s *SyncSuite) TestFailsWithDockerSourceNotExisting(c *check.C) {
|
||||
repo := path.Join(v2DockerRegistryURL, "imagedoesnotexist")
|
||||
tmpDir := t.TempDir()
|
||||
tmpDir := c.MkDir()
|
||||
|
||||
//untagged
|
||||
assertSkopeoFails(t, ".*repository name not known to registry.*",
|
||||
assertSkopeoFails(c, ".*repository name not known to registry.*",
|
||||
"sync", "--scoped", "--src-tls-verify=false", "--src", "docker", "--dest", "dir", repo, tmpDir)
|
||||
|
||||
//tagged
|
||||
assertSkopeoFails(t, ".*reading manifest.*",
|
||||
assertSkopeoFails(c, ".*reading manifest.*",
|
||||
"sync", "--scoped", "--src-tls-verify=false", "--src", "docker", "--dest", "dir", repo+":thetag", tmpDir)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestFailsWithDirSourceNotExisting() {
|
||||
t := s.T()
|
||||
func (s *SyncSuite) TestFailsWithDirSourceNotExisting(c *check.C) {
|
||||
// Make sure the dir does not exist!
|
||||
tmpDir := t.TempDir()
|
||||
tmpDir := c.MkDir()
|
||||
tmpDir = filepath.Join(tmpDir, "this-does-not-exist")
|
||||
err := os.RemoveAll(tmpDir)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
_, err = os.Stat(path.Join(tmpDir))
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
c.Check(os.IsNotExist(err), check.Equals, true)
|
||||
|
||||
assertSkopeoFails(t, ".*no such file or directory.*",
|
||||
assertSkopeoFails(c, ".*no such file or directory.*",
|
||||
"sync", "--scoped", "--dest-tls-verify=false", "--src", "dir", "--dest", "docker", tmpDir, v2DockerRegistryURL)
|
||||
}
|
||||
|
||||
@@ -4,17 +4,14 @@ import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
const skopeoBinary = "skopeo"
|
||||
@@ -24,19 +21,19 @@ const testFQIN = "docker://quay.io/libpod/busybox" // tag left off on purpose, s
|
||||
const testFQIN64 = "docker://quay.io/libpod/busybox:amd64"
|
||||
const testFQINMultiLayer = "docker://quay.io/libpod/alpine_nginx:latest" // multi-layer
|
||||
|
||||
// consumeAndLogOutputStream takes (f, err) from an exec.*Pipe(), and causes all output to it to be logged to t.
|
||||
func consumeAndLogOutputStream(t *testing.T, id string, f io.ReadCloser, err error) {
|
||||
require.NoError(t, err)
|
||||
// consumeAndLogOutputStream takes (f, err) from an exec.*Pipe(), and causes all output to it to be logged to c.
|
||||
func consumeAndLogOutputStream(c *check.C, id string, f io.ReadCloser, err error) {
|
||||
c.Assert(err, check.IsNil)
|
||||
go func() {
|
||||
defer func() {
|
||||
f.Close()
|
||||
t.Logf("Output %s: Closed", id)
|
||||
c.Logf("Output %s: Closed", id)
|
||||
}()
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
t.Logf("Output %s: waiting", id)
|
||||
c.Logf("Output %s: waiting", id)
|
||||
n, err := f.Read(buf)
|
||||
t.Logf("Output %s: got %d,%#v: %s", id, n, err, strings.TrimSuffix(string(buf[:n]), "\n"))
|
||||
c.Logf("Output %s: got %d,%#v: %s", id, n, err, strings.TrimSuffix(string(buf[:n]), "\n"))
|
||||
if n <= 0 {
|
||||
break
|
||||
}
|
||||
@@ -44,73 +41,72 @@ func consumeAndLogOutputStream(t *testing.T, id string, f io.ReadCloser, err err
|
||||
}()
|
||||
}
|
||||
|
||||
// consumeAndLogOutputs causes all output to stdout and stderr from an *exec.Cmd to be logged to c.
|
||||
func consumeAndLogOutputs(t *testing.T, id string, cmd *exec.Cmd) {
|
||||
// consumeAndLogOutputs causes all output to stdout and stderr from an *exec.Cmd to be logged to c
|
||||
func consumeAndLogOutputs(c *check.C, id string, cmd *exec.Cmd) {
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
consumeAndLogOutputStream(t, id+" stdout", stdout, err)
|
||||
consumeAndLogOutputStream(c, id+" stdout", stdout, err)
|
||||
stderr, err := cmd.StderrPipe()
|
||||
consumeAndLogOutputStream(t, id+" stderr", stderr, err)
|
||||
consumeAndLogOutputStream(c, id+" stderr", stderr, err)
|
||||
}
|
||||
|
||||
// combinedOutputOfCommand runs a command as if exec.Command().CombinedOutput(), verifies that the exit status is 0, and returns the output,
|
||||
// or terminates c on failure.
|
||||
func combinedOutputOfCommand(t *testing.T, name string, args ...string) string {
|
||||
t.Logf("Running %s %s", name, strings.Join(args, " "))
|
||||
func combinedOutputOfCommand(c *check.C, name string, args ...string) string {
|
||||
c.Logf("Running %s %s", name, strings.Join(args, " "))
|
||||
out, err := exec.Command(name, args...).CombinedOutput()
|
||||
require.NoError(t, err, "%s", out)
|
||||
c.Assert(err, check.IsNil, check.Commentf("%s", out))
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// assertSkopeoSucceeds runs a skopeo command as if exec.Command().CombinedOutput, verifies that the exit status is 0,
|
||||
// and optionally that the output matches a multi-line regexp if it is nonempty;
|
||||
// or terminates c on failure
|
||||
func assertSkopeoSucceeds(t *testing.T, regexp string, args ...string) {
|
||||
t.Logf("Running %s %s", skopeoBinary, strings.Join(args, " "))
|
||||
func assertSkopeoSucceeds(c *check.C, regexp string, args ...string) {
|
||||
c.Logf("Running %s %s", skopeoBinary, strings.Join(args, " "))
|
||||
out, err := exec.Command(skopeoBinary, args...).CombinedOutput()
|
||||
assert.NoError(t, err, "%s", out)
|
||||
c.Assert(err, check.IsNil, check.Commentf("%s", out))
|
||||
if regexp != "" {
|
||||
assert.Regexp(t, "(?s)"+regexp, string(out)) // (?s) : '.' will also match newlines
|
||||
c.Assert(string(out), check.Matches, "(?s)"+regexp) // (?s) : '.' will also match newlines
|
||||
}
|
||||
}
|
||||
|
||||
// assertSkopeoFails runs a skopeo command as if exec.Command().CombinedOutput, verifies that the exit status is 0,
|
||||
// and that the output matches a multi-line regexp;
|
||||
// or terminates c on failure
|
||||
func assertSkopeoFails(t *testing.T, regexp string, args ...string) {
|
||||
t.Logf("Running %s %s", skopeoBinary, strings.Join(args, " "))
|
||||
func assertSkopeoFails(c *check.C, regexp string, args ...string) {
|
||||
c.Logf("Running %s %s", skopeoBinary, strings.Join(args, " "))
|
||||
out, err := exec.Command(skopeoBinary, args...).CombinedOutput()
|
||||
assert.Error(t, err, "%s", out)
|
||||
assert.Regexp(t, "(?s)"+regexp, string(out)) // (?s) : '.' will also match newlines
|
||||
c.Assert(err, check.NotNil, check.Commentf("%s", out))
|
||||
c.Assert(string(out), check.Matches, "(?s)"+regexp) // (?s) : '.' will also match newlines
|
||||
}
|
||||
|
||||
// runCommandWithInput runs a command as if exec.Command(), sending it the input to stdin,
|
||||
// and verifies that the exit status is 0, or terminates c on failure.
|
||||
func runCommandWithInput(t *testing.T, input string, name string, args ...string) {
|
||||
func runCommandWithInput(c *check.C, input string, name string, args ...string) {
|
||||
cmd := exec.Command(name, args...)
|
||||
runExecCmdWithInput(t, cmd, input)
|
||||
runExecCmdWithInput(c, cmd, input)
|
||||
}
|
||||
|
||||
// runExecCmdWithInput runs an exec.Cmd, sending it the input to stdin,
|
||||
// and verifies that the exit status is 0, or terminates c on failure.
|
||||
func runExecCmdWithInput(t *testing.T, cmd *exec.Cmd, input string) {
|
||||
t.Logf("Running %s %s", cmd.Path, strings.Join(cmd.Args, " "))
|
||||
consumeAndLogOutputs(t, cmd.Path+" "+strings.Join(cmd.Args, " "), cmd)
|
||||
func runExecCmdWithInput(c *check.C, cmd *exec.Cmd, input string) {
|
||||
c.Logf("Running %s %s", cmd.Path, strings.Join(cmd.Args, " "))
|
||||
consumeAndLogOutputs(c, cmd.Path+" "+strings.Join(cmd.Args, " "), cmd)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
err = cmd.Start()
|
||||
require.NoError(t, err)
|
||||
_, err = io.WriteString(stdin, input)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
_, err = stdin.Write([]byte(input))
|
||||
c.Assert(err, check.IsNil)
|
||||
err = stdin.Close()
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
err = cmd.Wait()
|
||||
assert.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
}
|
||||
|
||||
// isPortOpen returns true iff the specified port on localhost is open.
|
||||
func isPortOpen(port uint16) bool {
|
||||
ap := netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), port)
|
||||
conn, err := net.DialTCP("tcp", nil, net.TCPAddrFromAddrPort(ap))
|
||||
func isPortOpen(port int) bool {
|
||||
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: port})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -122,29 +118,29 @@ func isPortOpen(port uint16) bool {
|
||||
// The checking can be aborted by sending a value to the terminate channel, which the caller should
|
||||
// always do using
|
||||
// defer func() {terminate <- true}()
|
||||
func newPortChecker(t *testing.T, port uint16) (portOpen <-chan bool, terminate chan<- bool) {
|
||||
func newPortChecker(c *check.C, port int) (portOpen <-chan bool, terminate chan<- bool) {
|
||||
portOpenBidi := make(chan bool)
|
||||
// Buffered, so that sending a terminate request after the goroutine has exited does not block.
|
||||
terminateBidi := make(chan bool, 1)
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
t.Logf("Port checker for port %d exiting", port)
|
||||
c.Logf("Port checker for port %d exiting", port)
|
||||
}()
|
||||
for {
|
||||
t.Logf("Checking for port %d...", port)
|
||||
c.Logf("Checking for port %d...", port)
|
||||
if isPortOpen(port) {
|
||||
t.Logf("Port %d open", port)
|
||||
c.Logf("Port %d open", port)
|
||||
portOpenBidi <- true
|
||||
return
|
||||
}
|
||||
t.Logf("Sleeping for port %d", port)
|
||||
c.Logf("Sleeping for port %d", port)
|
||||
sleepChan := time.After(100 * time.Millisecond)
|
||||
select {
|
||||
case <-sleepChan: // Try again
|
||||
t.Logf("Sleeping for port %d done, will retry", port)
|
||||
c.Logf("Sleeping for port %d done, will retry", port)
|
||||
case <-terminateBidi:
|
||||
t.Logf("Check for port %d terminated", port)
|
||||
c.Logf("Check for port %d terminated", port)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -166,51 +162,54 @@ func modifyEnviron(env []string, name, value string) []string {
|
||||
|
||||
// fileFromFixtureFixture applies edits to inputPath and returns a path to the temporary file.
|
||||
// Callers should defer os.Remove(the_returned_path)
|
||||
func fileFromFixture(t *testing.T, inputPath string, edits map[string]string) string {
|
||||
func fileFromFixture(c *check.C, inputPath string, edits map[string]string) string {
|
||||
contents, err := os.ReadFile(inputPath)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
for template, value := range edits {
|
||||
updated := bytes.ReplaceAll(contents, []byte(template), []byte(value))
|
||||
require.NotEqual(t, contents, updated, "Replacing %s in %#v failed", template, string(contents)) // Verify that the template has matched something and we are not silently ignoring it.
|
||||
c.Assert(bytes.Equal(updated, contents), check.Equals, false, check.Commentf("Replacing %s in %#v failed", template, string(contents))) // Verify that the template has matched something and we are not silently ignoring it.
|
||||
contents = updated
|
||||
}
|
||||
|
||||
file, err := os.CreateTemp("", "policy.json")
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
path := file.Name()
|
||||
|
||||
_, err = file.Write(contents)
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
err = file.Close()
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
return path
|
||||
}
|
||||
|
||||
// runDecompressDirs runs decompress-dirs.sh using exec.Command().CombinedOutput, verifies that the exit status is 0,
|
||||
// and optionally that the output matches a multi-line regexp if it is nonempty; or terminates c on failure
|
||||
func runDecompressDirs(t *testing.T, args ...string) {
|
||||
t.Logf("Running %s %s", decompressDirsBinary, strings.Join(args, " "))
|
||||
func runDecompressDirs(c *check.C, regexp string, args ...string) {
|
||||
c.Logf("Running %s %s", decompressDirsBinary, strings.Join(args, " "))
|
||||
for i, dir := range args {
|
||||
m, err := os.ReadFile(filepath.Join(dir, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
t.Logf("manifest %d before: %s", i+1, string(m))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Logf("manifest %d before: %s", i+1, string(m))
|
||||
}
|
||||
out, err := exec.Command(decompressDirsBinary, args...).CombinedOutput()
|
||||
assert.NoError(t, err, "%s", out)
|
||||
c.Assert(err, check.IsNil, check.Commentf("%s", out))
|
||||
for i, dir := range args {
|
||||
if len(out) > 0 {
|
||||
t.Logf("output: %s", out)
|
||||
c.Logf("output: %s", out)
|
||||
}
|
||||
m, err := os.ReadFile(filepath.Join(dir, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
t.Logf("manifest %d after: %s", i+1, string(m))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Logf("manifest %d after: %s", i+1, string(m))
|
||||
}
|
||||
if regexp != "" {
|
||||
c.Assert(string(out), check.Matches, "(?s)"+regexp) // (?s) : '.' will also match newlines
|
||||
}
|
||||
}
|
||||
|
||||
// Verify manifest in a dir: image at dir is expectedMIMEType.
|
||||
func verifyManifestMIMEType(t *testing.T, dir string, expectedMIMEType string) {
|
||||
func verifyManifestMIMEType(c *check.C, dir string, expectedMIMEType string) {
|
||||
manifestBlob, err := os.ReadFile(filepath.Join(dir, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
c.Assert(err, check.IsNil)
|
||||
mimeType := manifest.GuessMIMEType(manifestBlob)
|
||||
assert.Equal(t, expectedMIMEType, mimeType)
|
||||
c.Assert(mimeType, check.Equals, expectedMIMEType)
|
||||
}
|
||||
|
||||
@@ -9,14 +9,8 @@
|
||||
# CAUTION: This is not a replacement for RPMs provided by your distro.
|
||||
# Only intended to build and test the latest unreleased changes.
|
||||
|
||||
%global with_debug 1
|
||||
%global gomodulesmode GO111MODULE=on
|
||||
|
||||
# RHEL 8's default %%gobuild macro doesn't account for the BUILDTAGS variable, so we
|
||||
# set it separately here and do not depend on RHEL 8's go-srpm-macros package.
|
||||
%if !0%{?fedora} && 0%{?rhel} <= 8
|
||||
%define gobuild(o:) %{gomodulesmode} go build -buildmode pie -compiler gc -tags="rpm_crashtraceback libtrust_openssl ${BUILDTAGS:-}" -ldflags "-linkmode=external -compressdwarf=false ${LDFLAGS:-} -B 0x$(head -c20 /dev/urandom|od -An -tx1|tr -d ' \\n') -extldflags '%__global_ldflags'" -a -v -x %{?**};
|
||||
%endif
|
||||
%global with_debug 1
|
||||
|
||||
%if 0%{?with_debug}
|
||||
%global _find_debuginfo_dwz_opts %{nil}
|
||||
@@ -25,6 +19,10 @@
|
||||
%global debug_package %{nil}
|
||||
%endif
|
||||
|
||||
%if ! 0%{?gobuild:1}
|
||||
%define gobuild(o:) go build -buildmode pie -compiler gc -tags="rpm_crashtraceback ${BUILDTAGS:-}" -ldflags "${LDFLAGS:-} -B 0x$(head -c20 /dev/urandom|od -An -tx1|tr -d ' \\n') -extldflags '-Wl,-z,relro -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld '" -a -v -x %{?**};
|
||||
%endif
|
||||
|
||||
Name: {{{ git_dir_name }}}
|
||||
Epoch: 101
|
||||
Version: {{{ git_dir_version }}}
|
||||
@@ -50,7 +48,11 @@ BuildRequires: libassuan-devel
|
||||
BuildRequires: pkgconfig
|
||||
BuildRequires: make
|
||||
BuildRequires: ostree-devel
|
||||
Requires: containers-common >= 4:1-78
|
||||
%if 0%{?fedora} <= 35
|
||||
Requires: containers-common >= 4:1-39
|
||||
%else
|
||||
Requires: containers-common >= 4:1-46
|
||||
%endif
|
||||
|
||||
%description
|
||||
Command line utility to inspect images and repositories directly on Docker
|
||||
@@ -93,7 +95,11 @@ export CGO_CFLAGS+=" -m64 -mtune=generic -fcf-protection=full"
|
||||
|
||||
LDFLAGS=""
|
||||
|
||||
export BUILDTAGS="$(hack/libdm_tag.sh) $(hack/btrfs_installed_tag.sh) $(hack/btrfs_tag.sh)"
|
||||
export BUILDTAGS="$(hack/libdm_tag.sh)"
|
||||
%if 0%{?rhel}
|
||||
export BUILDTAGS="$BUILDTAGS exclude_graphdriver_btrfs btrfs_noversion"
|
||||
%endif
|
||||
|
||||
%gobuild -o bin/%{name} ./cmd/%{name}
|
||||
|
||||
%install
|
||||
|
||||
@@ -16,4 +16,29 @@ function setup() {
|
||||
expect_output --substring "skopeo version [0-9.]+"
|
||||
}
|
||||
|
||||
@test "skopeo release isn't a development version" {
|
||||
[[ "${RELEASE_TESTING:-false}" == "true" ]] || \
|
||||
skip "Release testing may be enabled by setting \$RELEASE_TESTING = 'true'."
|
||||
|
||||
run_skopeo --version
|
||||
|
||||
# expect_output() doesn't support negative matching
|
||||
if [[ "$output" =~ "dev" ]]; then
|
||||
# This is a multi-line message, which may in turn contain multi-line
|
||||
# output, so let's format it ourselves, readably
|
||||
local -a output_split
|
||||
readarray -t output_split <<<"$output"
|
||||
printf "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n" >&2
|
||||
printf "#| FAIL: $BATS_TEST_NAME\n" >&2
|
||||
printf "#| unexpected: 'dev'\n" >&2
|
||||
printf "#| actual: '%s'\n" "${output_split[0]}" >&2
|
||||
local line
|
||||
for line in "${output_split[@]:1}"; do
|
||||
printf "#| > '%s'\n" "$line" >&2
|
||||
done
|
||||
printf "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" >&2
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
# vim: filetype=sh
|
||||
|
||||
@@ -16,8 +16,7 @@ function setup() {
|
||||
|
||||
_cred_dir=$TESTDIR/credentials
|
||||
# It is important to change XDG_RUNTIME_DIR only after we start the registry, otherwise it affects the path of $XDG_RUNTIME_DIR/netns maintained by Podman,
|
||||
# making it impossible to clean up after ourselves.
|
||||
export XDG_RUNTIME_DIR_OLD=$XDG_RUNTIME_DIR
|
||||
# making it imposible to clean up after ourselves.
|
||||
export XDG_RUNTIME_DIR=$_cred_dir
|
||||
mkdir -p $_cred_dir/containers
|
||||
# Remove old/stale cred file
|
||||
@@ -112,9 +111,6 @@ function setup() {
|
||||
}
|
||||
|
||||
teardown() {
|
||||
# Need to restore XDG_RUNTIME_DIR.
|
||||
XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR_OLD
|
||||
|
||||
podman rm -f reg
|
||||
|
||||
if [[ -n $_cred_dir ]]; then
|
||||
|
||||
@@ -242,7 +242,7 @@ END_TESTS
|
||||
$fingerprint \
|
||||
$TESTDIR/busybox.signature
|
||||
# manifest digest
|
||||
digest=$(echo "$output" | awk '{print $NF;}')
|
||||
digest=$(echo "$output" | awk '{print $4;}')
|
||||
run_skopeo manifest-digest $TESTDIR/busybox/manifest.json
|
||||
expect_output $digest
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ SKOPEO_BINARY=${SKOPEO_BINARY:-${TEST_SOURCE_DIR}/../bin/skopeo}
|
||||
SKOPEO_TIMEOUT=${SKOPEO_TIMEOUT:-300}
|
||||
|
||||
# Default image to run as a local registry
|
||||
REGISTRY_FQIN=${SKOPEO_TEST_REGISTRY_FQIN:-quay.io/libpod/registry:2}
|
||||
REGISTRY_FQIN=${SKOPEO_TEST_REGISTRY_FQIN:-quay.io/libpod/registry:2.8.2}
|
||||
|
||||
###############################################################################
|
||||
# BEGIN setup/teardown
|
||||
|
||||
@@ -20,43 +20,34 @@ type (
|
||||
securityInformation uint32
|
||||
trusteeForm uint32
|
||||
trusteeType uint32
|
||||
|
||||
//nolint:structcheck // structcheck thinks fields are unused, but the are used to pass data to OS
|
||||
explicitAccess struct {
|
||||
accessPermissions accessMask
|
||||
accessMode accessMode
|
||||
inheritance inheritMode
|
||||
trustee trustee
|
||||
}
|
||||
|
||||
//nolint:structcheck,unused // structcheck thinks fields are unused, but the are used to pass data to OS
|
||||
trustee struct {
|
||||
multipleTrustee *trustee
|
||||
multipleTrusteeOperation int32
|
||||
trusteeForm trusteeForm
|
||||
trusteeType trusteeType
|
||||
name uintptr
|
||||
}
|
||||
)
|
||||
|
||||
type explicitAccess struct {
|
||||
//nolint:structcheck
|
||||
accessPermissions accessMask
|
||||
//nolint:structcheck
|
||||
accessMode accessMode
|
||||
//nolint:structcheck
|
||||
inheritance inheritMode
|
||||
//nolint:structcheck
|
||||
trustee trustee
|
||||
}
|
||||
|
||||
type trustee struct {
|
||||
//nolint:unused,structcheck
|
||||
multipleTrustee *trustee
|
||||
//nolint:unused,structcheck
|
||||
multipleTrusteeOperation int32
|
||||
trusteeForm trusteeForm
|
||||
trusteeType trusteeType
|
||||
name uintptr
|
||||
}
|
||||
|
||||
const (
|
||||
AccessMaskNone accessMask = 0
|
||||
AccessMaskRead accessMask = 1 << 31 // GENERIC_READ
|
||||
AccessMaskWrite accessMask = 1 << 30 // GENERIC_WRITE
|
||||
AccessMaskExecute accessMask = 1 << 29 // GENERIC_EXECUTE
|
||||
AccessMaskAll accessMask = 1 << 28 // GENERIC_ALL
|
||||
|
||||
accessMaskDesiredPermission = AccessMaskRead
|
||||
accessMaskDesiredPermission accessMask = 1 << 31 // GENERIC_READ
|
||||
|
||||
accessModeGrant accessMode = 1
|
||||
|
||||
desiredAccessReadControl desiredAccess = 0x20000
|
||||
desiredAccessWriteDac desiredAccess = 0x40000
|
||||
|
||||
//cspell:disable-next-line
|
||||
gvmga = "GrantVmGroupAccess:"
|
||||
|
||||
inheritModeNoInheritance inheritMode = 0x0
|
||||
@@ -69,28 +60,20 @@ const (
|
||||
shareModeRead shareMode = 0x1
|
||||
shareModeWrite shareMode = 0x2
|
||||
|
||||
//nolint:stylecheck // ST1003
|
||||
sidVmGroup = "S-1-5-83-0"
|
||||
sidVMGroup = "S-1-5-83-0"
|
||||
|
||||
trusteeFormIsSid trusteeForm = 0
|
||||
trusteeFormIsSID trusteeForm = 0
|
||||
|
||||
trusteeTypeWellKnownGroup trusteeType = 5
|
||||
)
|
||||
|
||||
// GrantVmGroupAccess sets the DACL for a specified file or directory to
|
||||
// GrantVMGroupAccess sets the DACL for a specified file or directory to
|
||||
// include Grant ACE entries for the VM Group SID. This is a golang re-
|
||||
// implementation of the same function in vmcompute, just not exported in
|
||||
// RS5. Which kind of sucks. Sucks a lot :/
|
||||
func GrantVmGroupAccess(name string) error { //nolint:stylecheck // ST1003
|
||||
return GrantVmGroupAccessWithMask(name, accessMaskDesiredPermission)
|
||||
}
|
||||
|
||||
// GrantVmGroupAccessWithMask sets the desired DACL for a specified file or
|
||||
// directory.
|
||||
func GrantVmGroupAccessWithMask(name string, access accessMask) error { //nolint:stylecheck // ST1003
|
||||
if access == 0 || access<<4 != 0 {
|
||||
return fmt.Errorf("invalid access mask: 0x%08x", access)
|
||||
}
|
||||
//
|
||||
//revive:disable-next-line:var-naming VM, not Vm
|
||||
func GrantVmGroupAccess(name string) error {
|
||||
// Stat (to determine if `name` is a directory).
|
||||
s, err := os.Stat(name)
|
||||
if err != nil {
|
||||
@@ -102,9 +85,7 @@ func GrantVmGroupAccessWithMask(name string, access accessMask) error { //nolint
|
||||
if err != nil {
|
||||
return err // Already wrapped
|
||||
}
|
||||
defer func() {
|
||||
_ = syscall.CloseHandle(fd)
|
||||
}()
|
||||
defer syscall.CloseHandle(fd) //nolint:errcheck
|
||||
|
||||
// Get the current DACL and Security Descriptor. Must defer LocalFree on success.
|
||||
ot := objectTypeFileObject
|
||||
@@ -114,19 +95,15 @@ func GrantVmGroupAccessWithMask(name string, access accessMask) error { //nolint
|
||||
if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil {
|
||||
return fmt.Errorf("%s GetSecurityInfo %s: %w", gvmga, name, err)
|
||||
}
|
||||
defer func() {
|
||||
_, _ = syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd)))
|
||||
}()
|
||||
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd))) //nolint:errcheck
|
||||
|
||||
// Generate a new DACL which is the current DACL with the required ACEs added.
|
||||
// Must defer LocalFree on success.
|
||||
newDACL, err := generateDACLWithAcesAdded(name, s.IsDir(), access, origDACL)
|
||||
newDACL, err := generateDACLWithAcesAdded(name, s.IsDir(), origDACL)
|
||||
if err != nil {
|
||||
return err // Already wrapped
|
||||
}
|
||||
defer func() {
|
||||
_, _ = syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL)))
|
||||
}()
|
||||
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL))) //nolint:errcheck
|
||||
|
||||
// And finally use SetSecurityInfo to apply the updated DACL.
|
||||
if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil {
|
||||
@@ -141,28 +118,28 @@ func GrantVmGroupAccessWithMask(name string, access accessMask) error { //nolint
|
||||
func createFile(name string, isDir bool) (syscall.Handle, error) {
|
||||
namep, err := syscall.UTF16FromString(name)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("syscall.UTF16FromString %s: %w", name, err)
|
||||
return syscall.InvalidHandle, fmt.Errorf("could not convernt name to UTF-16: %w", err)
|
||||
}
|
||||
da := uint32(desiredAccessReadControl | desiredAccessWriteDac)
|
||||
sm := uint32(shareModeRead | shareModeWrite)
|
||||
fa := uint32(syscall.FILE_ATTRIBUTE_NORMAL)
|
||||
if isDir {
|
||||
fa = uint32(fa | syscall.FILE_FLAG_BACKUP_SEMANTICS)
|
||||
fa |= syscall.FILE_FLAG_BACKUP_SEMANTICS
|
||||
}
|
||||
fd, err := syscall.CreateFile(&namep[0], da, sm, nil, syscall.OPEN_EXISTING, fa, 0)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%s syscall.CreateFile %s: %w", gvmga, name, err)
|
||||
return syscall.InvalidHandle, fmt.Errorf("%s syscall.CreateFile %s: %w", gvmga, name, err)
|
||||
}
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
// generateDACLWithAcesAdded generates a new DACL with the two needed ACEs added.
|
||||
// The caller is responsible for LocalFree of the returned DACL on success.
|
||||
func generateDACLWithAcesAdded(name string, isDir bool, desiredAccess accessMask, origDACL uintptr) (uintptr, error) {
|
||||
func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintptr, error) {
|
||||
// Generate pointers to the SIDs based on the string SIDs
|
||||
sid, err := syscall.StringToSid(sidVmGroup)
|
||||
sid, err := syscall.StringToSid(sidVMGroup)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%s syscall.StringToSid %s %s: %w", gvmga, name, sidVmGroup, err)
|
||||
return 0, fmt.Errorf("%s syscall.StringToSid %s %s: %w", gvmga, name, sidVMGroup, err)
|
||||
}
|
||||
|
||||
inheritance := inheritModeNoInheritance
|
||||
@@ -172,11 +149,11 @@ func generateDACLWithAcesAdded(name string, isDir bool, desiredAccess accessMask
|
||||
|
||||
eaArray := []explicitAccess{
|
||||
{
|
||||
accessPermissions: desiredAccess,
|
||||
accessPermissions: accessMaskDesiredPermission,
|
||||
accessMode: accessModeGrant,
|
||||
inheritance: inheritance,
|
||||
trustee: trustee{
|
||||
trusteeForm: trusteeFormIsSid,
|
||||
trusteeForm: trusteeFormIsSID,
|
||||
trusteeType: trusteeTypeWellKnownGroup,
|
||||
name: uintptr(unsafe.Pointer(sid)),
|
||||
},
|
||||
57
vendor/github.com/Microsoft/go-winio/tools/mkwinsyscall/doc.go
generated
vendored
57
vendor/github.com/Microsoft/go-winio/tools/mkwinsyscall/doc.go
generated
vendored
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
mkwinsyscall generates windows system call bodies
|
||||
|
||||
It parses all files specified on command line containing function
|
||||
prototypes (like syscall_windows.go) and prints system call bodies
|
||||
to standard output.
|
||||
|
||||
The prototypes are marked by lines beginning with "//sys" and read
|
||||
like func declarations if //sys is replaced by func, but:
|
||||
|
||||
- The parameter lists must give a name for each argument. This
|
||||
includes return parameters.
|
||||
|
||||
- The parameter lists must give a type for each argument:
|
||||
the (x, y, z int) shorthand is not allowed.
|
||||
|
||||
- If the return parameter is an error number, it must be named err.
|
||||
|
||||
- If go func name needs to be different from its winapi dll name,
|
||||
the winapi name could be specified at the end, after "=" sign, like
|
||||
|
||||
//sys LoadLibrary(libname string) (handle uint32, err error) = LoadLibraryA
|
||||
|
||||
- Each function that returns err needs to supply a condition, that
|
||||
return value of winapi will be tested against to detect failure.
|
||||
This would set err to windows "last-error", otherwise it will be nil.
|
||||
The value can be provided at end of //sys declaration, like
|
||||
|
||||
//sys LoadLibrary(libname string) (handle uint32, err error) [failretval==-1] = LoadLibraryA
|
||||
|
||||
and is [failretval==0] by default.
|
||||
|
||||
- If the function name ends in a "?", then the function not existing is non-
|
||||
fatal, and an error will be returned instead of panicking.
|
||||
|
||||
Usage:
|
||||
|
||||
mkwinsyscall [flags] [path ...]
|
||||
|
||||
Flags
|
||||
|
||||
-output string
|
||||
Output file name (standard output if omitted).
|
||||
-sort
|
||||
Sort DLL and function declarations (default true).
|
||||
Intended to help transition from older versions of mkwinsyscall by making diffs
|
||||
easier to read and understand.
|
||||
-systemdll
|
||||
Whether all DLLs should be loaded from the Windows system directory (default true).
|
||||
-trace
|
||||
Generate print statement after every syscall.
|
||||
-utf16
|
||||
Encode string arguments as UTF-16 for syscalls not ending in 'A' or 'W' (default true).
|
||||
-winio
|
||||
Import this package ("github.com/Microsoft/go-winio").
|
||||
*/
|
||||
package main
|
||||
1014
vendor/github.com/Microsoft/go-winio/tools/mkwinsyscall/mkwinsyscall.go
generated
vendored
1014
vendor/github.com/Microsoft/go-winio/tools/mkwinsyscall/mkwinsyscall.go
generated
vendored
File diff suppressed because it is too large
Load Diff
4
vendor/github.com/Microsoft/hcsshim/.gitattributes
generated
vendored
4
vendor/github.com/Microsoft/hcsshim/.gitattributes
generated
vendored
@@ -1,3 +1 @@
|
||||
* text=auto eol=lf
|
||||
vendor/** -text
|
||||
test/vendor/** -text
|
||||
* text=auto eol=lf
|
||||
15
vendor/github.com/Microsoft/hcsshim/.gitignore
generated
vendored
15
vendor/github.com/Microsoft/hcsshim/.gitignore
generated
vendored
@@ -6,7 +6,6 @@
|
||||
|
||||
# Ignore vscode setting files
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
@@ -24,26 +23,16 @@ service/pkg/
|
||||
*.img
|
||||
*.vhd
|
||||
*.tar.gz
|
||||
*.tar
|
||||
|
||||
# Make stuff
|
||||
.rootfs-done
|
||||
bin/*
|
||||
rootfs/*
|
||||
rootfs-conv/*
|
||||
*.o
|
||||
/build/
|
||||
|
||||
deps/*
|
||||
out/*
|
||||
|
||||
# test results
|
||||
test/results
|
||||
|
||||
# go workspace files
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# keys and related artifacts
|
||||
*.pem
|
||||
*.cose
|
||||
.idea/
|
||||
.vscode/
|
||||
58
vendor/github.com/Microsoft/hcsshim/.golangci.yml
generated
vendored
58
vendor/github.com/Microsoft/hcsshim/.golangci.yml
generated
vendored
@@ -1,51 +1,23 @@
|
||||
run:
|
||||
timeout: 8m
|
||||
tests: true
|
||||
build-tags:
|
||||
- admin
|
||||
- functional
|
||||
- integration
|
||||
skip-dirs:
|
||||
# paths are relative to module root
|
||||
- cri-containerd/test-images
|
||||
|
||||
linters:
|
||||
enable:
|
||||
# defaults:
|
||||
# - errcheck
|
||||
# - gosimple
|
||||
# - govet
|
||||
# - ineffassign
|
||||
# - staticcheck
|
||||
# - typecheck
|
||||
# - unused
|
||||
|
||||
- gofmt # whether code was gofmt-ed
|
||||
- nolintlint # ill-formed or insufficient nolint directives
|
||||
- stylecheck # golint replacement
|
||||
- thelper # test helpers without t.Helper()
|
||||
- stylecheck
|
||||
|
||||
linters-settings:
|
||||
stylecheck:
|
||||
# https://staticcheck.io/docs/checks
|
||||
checks: ["all"]
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
# path is relative to module root, which is ./test/
|
||||
- path: cri-containerd
|
||||
linters:
|
||||
- stylecheck
|
||||
text: "^ST1003: should not use underscores in package names$"
|
||||
source: "^package cri_containerd$"
|
||||
|
||||
# This repo has a LOT of generated schema files, operating system bindings, and other
|
||||
# things that ST1003 from stylecheck won't like (screaming case Windows api constants for example).
|
||||
# There's also some structs that we *could* change the initialisms to be Go friendly
|
||||
# (Id -> ID) but they're exported and it would be a breaking change.
|
||||
# This makes it so that most new code, code that isn't supposed to be a pretty faithful
|
||||
# mapping to an OS call/constants, or non-generated code still checks if we're following idioms,
|
||||
# while ignoring the things that are just noise or would be more of a hassle than it'd be worth to change.
|
||||
issues:
|
||||
# This repo has a LOT of generated schema files, operating system bindings, and other things that ST1003 from stylecheck won't like
|
||||
# (screaming case Windows api constants for example). There's also some structs that we *could* change the initialisms to be Go
|
||||
# friendly (Id -> ID) but they're exported and it would be a breaking change. This makes it so that most new code, code that isn't
|
||||
# supposed to be a pretty faithful mapping to an OS call/constants, or non-generated code still checks if we're following idioms,
|
||||
# while ignoring the things that are just noise or would be more of a hassle than it'd be worth to change.
|
||||
exclude-rules:
|
||||
- path: layer.go
|
||||
linters:
|
||||
- stylecheck
|
||||
@@ -56,20 +28,10 @@ issues:
|
||||
- stylecheck
|
||||
Text: "ST1003:"
|
||||
|
||||
- path: cmd\\ncproxy\\nodenetsvc\\
|
||||
linters:
|
||||
- stylecheck
|
||||
Text: "ST1003:"
|
||||
|
||||
- path: cmd\\ncproxy_mock\\
|
||||
linters:
|
||||
- stylecheck
|
||||
Text: "ST1003:"
|
||||
|
||||
- path: internal\\hcs\\schema2\\
|
||||
linters:
|
||||
- stylecheck
|
||||
- gofmt
|
||||
Text: "ST1003:"
|
||||
|
||||
- path: internal\\wclayer\\
|
||||
linters:
|
||||
@@ -134,4 +96,4 @@ issues:
|
||||
- path: internal\\hcserror\\
|
||||
linters:
|
||||
- stylecheck
|
||||
Text: "ST1003:"
|
||||
Text: "ST1003:"
|
||||
76
vendor/github.com/Microsoft/hcsshim/Makefile
generated
vendored
76
vendor/github.com/Microsoft/hcsshim/Makefile
generated
vendored
@@ -1,5 +1,4 @@
|
||||
BASE:=base.tar.gz
|
||||
DEV_BUILD:=0
|
||||
|
||||
GO:=go
|
||||
GO_FLAGS:=-ldflags "-s -w" # strip Go binaries
|
||||
@@ -13,31 +12,16 @@ GO_FLAGS_EXTRA:=
|
||||
ifeq "$(GOMODVENDOR)" "1"
|
||||
GO_FLAGS_EXTRA += -mod=vendor
|
||||
endif
|
||||
GO_BUILD_TAGS:=
|
||||
ifneq ($(strip $(GO_BUILD_TAGS)),)
|
||||
GO_FLAGS_EXTRA += -tags="$(GO_BUILD_TAGS)"
|
||||
endif
|
||||
GO_BUILD:=CGO_ENABLED=$(CGO_ENABLED) $(GO) build $(GO_FLAGS) $(GO_FLAGS_EXTRA)
|
||||
|
||||
SRCROOT=$(dir $(abspath $(firstword $(MAKEFILE_LIST))))
|
||||
# additional directories to search for rule prerequisites and targets
|
||||
VPATH=$(SRCROOT)
|
||||
|
||||
DELTA_TARGET=out/delta.tar.gz
|
||||
|
||||
ifeq "$(DEV_BUILD)" "1"
|
||||
DELTA_TARGET=out/delta-dev.tar.gz
|
||||
endif
|
||||
|
||||
# The link aliases for gcstools
|
||||
GCS_TOOLS=\
|
||||
generichook \
|
||||
install-drivers
|
||||
generichook
|
||||
|
||||
.PHONY: all always rootfs test
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
all: out/initrd.img out/rootfs.tar.gz
|
||||
|
||||
clean:
|
||||
@@ -45,13 +29,21 @@ clean:
|
||||
rm -rf bin deps rootfs out
|
||||
|
||||
test:
|
||||
cd $(SRCROOT) && $(GO) test -v ./internal/guest/...
|
||||
cd $(SRCROOT) && go test -v ./internal/guest/...
|
||||
|
||||
rootfs: out/rootfs.vhd
|
||||
|
||||
out/rootfs.vhd: out/rootfs.tar.gz bin/cmd/tar2ext4
|
||||
gzip -f -d ./out/rootfs.tar.gz
|
||||
bin/cmd/tar2ext4 -vhd -i ./out/rootfs.tar -o $@
|
||||
out/delta.tar.gz: bin/init bin/vsockexec bin/cmd/gcs bin/cmd/gcstools Makefile
|
||||
@mkdir -p out
|
||||
rm -rf rootfs
|
||||
mkdir -p rootfs/bin/
|
||||
cp bin/init rootfs/
|
||||
cp bin/vsockexec rootfs/bin/
|
||||
cp bin/cmd/gcs rootfs/bin/
|
||||
cp bin/cmd/gcstools rootfs/bin/
|
||||
for tool in $(GCS_TOOLS); do ln -s gcstools rootfs/bin/$$tool; done
|
||||
git -C $(SRCROOT) rev-parse HEAD > rootfs/gcs.commit && \
|
||||
git -C $(SRCROOT) rev-parse --abbrev-ref HEAD > rootfs/gcs.branch
|
||||
tar -zcf $@ -C rootfs .
|
||||
rm -rf rootfs
|
||||
|
||||
out/rootfs.tar.gz: out/initrd.img
|
||||
rm -rf rootfs-conv
|
||||
@@ -60,45 +52,13 @@ out/rootfs.tar.gz: out/initrd.img
|
||||
tar -zcf $@ -C rootfs-conv .
|
||||
rm -rf rootfs-conv
|
||||
|
||||
out/initrd.img: $(BASE) $(DELTA_TARGET) $(SRCROOT)/hack/catcpio.sh
|
||||
$(SRCROOT)/hack/catcpio.sh "$(BASE)" $(DELTA_TARGET) > out/initrd.img.uncompressed
|
||||
out/initrd.img: $(BASE) out/delta.tar.gz $(SRCROOT)/hack/catcpio.sh
|
||||
$(SRCROOT)/hack/catcpio.sh "$(BASE)" out/delta.tar.gz > out/initrd.img.uncompressed
|
||||
gzip -c out/initrd.img.uncompressed > $@
|
||||
rm out/initrd.img.uncompressed
|
||||
|
||||
# This target includes utilities which may be useful for testing purposes.
|
||||
out/delta-dev.tar.gz: out/delta.tar.gz bin/internal/tools/snp-report
|
||||
rm -rf rootfs-dev
|
||||
mkdir rootfs-dev
|
||||
tar -xzf out/delta.tar.gz -C rootfs-dev
|
||||
cp bin/internal/tools/snp-report rootfs-dev/bin/
|
||||
tar -zcf $@ -C rootfs-dev .
|
||||
rm -rf rootfs-dev
|
||||
|
||||
out/delta.tar.gz: bin/init bin/vsockexec bin/cmd/gcs bin/cmd/gcstools bin/cmd/hooks/wait-paths Makefile
|
||||
@mkdir -p out
|
||||
rm -rf rootfs
|
||||
mkdir -p rootfs/bin/
|
||||
mkdir -p rootfs/info/
|
||||
cp bin/init rootfs/
|
||||
cp bin/vsockexec rootfs/bin/
|
||||
cp bin/cmd/gcs rootfs/bin/
|
||||
cp bin/cmd/gcstools rootfs/bin/
|
||||
cp bin/cmd/hooks/wait-paths rootfs/bin/
|
||||
for tool in $(GCS_TOOLS); do ln -s gcstools rootfs/bin/$$tool; done
|
||||
git -C $(SRCROOT) rev-parse HEAD > rootfs/info/gcs.commit && \
|
||||
git -C $(SRCROOT) rev-parse --abbrev-ref HEAD > rootfs/info/gcs.branch && \
|
||||
date --iso-8601=minute --utc > rootfs/info/tar.date
|
||||
$(if $(and $(realpath $(subst .tar,.testdata.json,$(BASE))), $(shell which jq)), \
|
||||
jq -r '.IMAGE_NAME' $(subst .tar,.testdata.json,$(BASE)) 2>/dev/null > rootfs/info/image.name && \
|
||||
jq -r '.DATETIME' $(subst .tar,.testdata.json,$(BASE)) 2>/dev/null > rootfs/info/build.date)
|
||||
tar -zcf $@ -C rootfs .
|
||||
rm -rf rootfs
|
||||
|
||||
-include deps/cmd/gcs.gomake
|
||||
-include deps/cmd/gcstools.gomake
|
||||
-include deps/cmd/hooks/wait-paths.gomake
|
||||
-include deps/cmd/tar2ext4.gomake
|
||||
-include deps/internal/tools/snp-report.gomake
|
||||
|
||||
# Implicit rule for includes that define Go targets.
|
||||
%.gomake: $(SRCROOT)/Makefile
|
||||
@@ -112,6 +72,8 @@ out/delta.tar.gz: bin/init bin/vsockexec bin/cmd/gcs bin/cmd/gcstools bin/cmd/ho
|
||||
@/bin/echo -e '-include $(@:%.gomake=%.godeps)' >> $@.new
|
||||
mv $@.new $@
|
||||
|
||||
VPATH=$(SRCROOT)
|
||||
|
||||
bin/vsockexec: vsockexec/vsockexec.o vsockexec/vsock.o
|
||||
@mkdir -p bin
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
11
vendor/github.com/Microsoft/hcsshim/Protobuild.toml
generated
vendored
11
vendor/github.com/Microsoft/hcsshim/Protobuild.toml
generated
vendored
@@ -1,4 +1,4 @@
|
||||
version = "1"
|
||||
version = "unstable"
|
||||
generator = "gogoctrd"
|
||||
plugins = ["grpc", "fieldpath"]
|
||||
|
||||
@@ -14,6 +14,11 @@ plugins = ["grpc", "fieldpath"]
|
||||
# target package.
|
||||
packages = ["github.com/gogo/protobuf"]
|
||||
|
||||
# Paths that will be added untouched to the end of the includes. We use
|
||||
# `/usr/local/include` to pickup the common install location of protobuf.
|
||||
# This is the default.
|
||||
after = ["/usr/local/include"]
|
||||
|
||||
# This section maps protobuf imports to Go packages. These will become
|
||||
# `-M` directives in the call to the go protobuf generator.
|
||||
[packages]
|
||||
@@ -31,10 +36,6 @@ plugins = ["grpc", "fieldpath"]
|
||||
prefixes = ["github.com/Microsoft/hcsshim/internal/shimdiag"]
|
||||
plugins = ["ttrpc"]
|
||||
|
||||
[[overrides]]
|
||||
prefixes = ["github.com/Microsoft/hcsshim/internal/extendedtask"]
|
||||
plugins = ["ttrpc"]
|
||||
|
||||
[[overrides]]
|
||||
prefixes = ["github.com/Microsoft/hcsshim/internal/computeagent"]
|
||||
plugins = ["ttrpc"]
|
||||
|
||||
20
vendor/github.com/Microsoft/hcsshim/README.md
generated
vendored
20
vendor/github.com/Microsoft/hcsshim/README.md
generated
vendored
@@ -75,6 +75,24 @@ certify they either authored the work themselves or otherwise have permission to
|
||||
more info, as well as to make sure that you can attest to the rules listed. Our CI uses the [DCO Github app](https://github.com/apps/dco) to ensure
|
||||
that all commits in a given PR are signed-off.
|
||||
|
||||
### Test Directory (Important to note)
|
||||
|
||||
This project has tried to trim some dependencies from the root Go modules file that would be cumbersome to get transitively included if this
|
||||
project is being vendored/used as a library. Some of these dependencies were only being used for tests, so the /test directory in this project also has
|
||||
its own go.mod file where these are now included to get around this issue. Our tests rely on the code in this project to run, so the test Go modules file
|
||||
has a relative path replace directive to pull in the latest hcsshim code that the tests actually touch from this project
|
||||
(which is the repo itself on your disk).
|
||||
|
||||
```
|
||||
replace (
|
||||
github.com/Microsoft/hcsshim => ../
|
||||
)
|
||||
```
|
||||
|
||||
Because of this, for most code changes you may need to run `go mod vendor` + `go mod tidy` in the /test directory in this repository, as the
|
||||
CI in this project will check if the files are out of date and will fail if this is true.
|
||||
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
@@ -83,7 +101,7 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio
|
||||
|
||||
## Dependencies
|
||||
|
||||
This project requires Golang 1.17 or newer to build.
|
||||
This project requires Golang 1.9 or newer to build.
|
||||
|
||||
For system requirements to run this project, see the Microsoft docs on [Windows Container requirements](https://docs.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/system-requirements).
|
||||
|
||||
|
||||
41
vendor/github.com/Microsoft/hcsshim/SECURITY.md
generated
vendored
41
vendor/github.com/Microsoft/hcsshim/SECURITY.md
generated
vendored
@@ -1,41 +0,0 @@
|
||||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.7 BLOCK -->
|
||||
|
||||
## Security
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
|
||||
|
||||
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
|
||||
|
||||
## Preferred Languages
|
||||
|
||||
We prefer all communications to be in English.
|
||||
|
||||
## Policy
|
||||
|
||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
|
||||
|
||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||
6
vendor/github.com/Microsoft/hcsshim/computestorage/attach.go
generated
vendored
6
vendor/github.com/Microsoft/hcsshim/computestorage/attach.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package computestorage
|
||||
|
||||
import (
|
||||
@@ -19,8 +17,8 @@ import (
|
||||
//
|
||||
// `layerData` is the parent read-only layer data.
|
||||
func AttachLayerStorageFilter(ctx context.Context, layerPath string, layerData LayerData) (err error) {
|
||||
title := "hcsshim::AttachLayerStorageFilter"
|
||||
ctx, span := oc.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
title := "hcsshim.AttachLayerStorageFilter"
|
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End()
|
||||
defer func() { oc.SetSpanStatus(span, err) }()
|
||||
span.AddAttributes(
|
||||
|
||||
6
vendor/github.com/Microsoft/hcsshim/computestorage/destroy.go
generated
vendored
6
vendor/github.com/Microsoft/hcsshim/computestorage/destroy.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package computestorage
|
||||
|
||||
import (
|
||||
@@ -14,8 +12,8 @@ import (
|
||||
//
|
||||
// `layerPath` is a path to a directory containing the layer to export.
|
||||
func DestroyLayer(ctx context.Context, layerPath string) (err error) {
|
||||
title := "hcsshim::DestroyLayer"
|
||||
ctx, span := oc.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
title := "hcsshim.DestroyLayer"
|
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End()
|
||||
defer func() { oc.SetSpanStatus(span, err) }()
|
||||
span.AddAttributes(trace.StringAttribute("layerPath", layerPath))
|
||||
|
||||
6
vendor/github.com/Microsoft/hcsshim/computestorage/detach.go
generated
vendored
6
vendor/github.com/Microsoft/hcsshim/computestorage/detach.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package computestorage
|
||||
|
||||
import (
|
||||
@@ -14,8 +12,8 @@ import (
|
||||
//
|
||||
// `layerPath` is a path to a directory containing the layer to export.
|
||||
func DetachLayerStorageFilter(ctx context.Context, layerPath string) (err error) {
|
||||
title := "hcsshim::DetachLayerStorageFilter"
|
||||
ctx, span := oc.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
title := "hcsshim.DetachLayerStorageFilter"
|
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End()
|
||||
defer func() { oc.SetSpanStatus(span, err) }()
|
||||
span.AddAttributes(trace.StringAttribute("layerPath", layerPath))
|
||||
|
||||
12
vendor/github.com/Microsoft/hcsshim/computestorage/export.go
generated
vendored
12
vendor/github.com/Microsoft/hcsshim/computestorage/export.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package computestorage
|
||||
|
||||
import (
|
||||
@@ -21,8 +19,8 @@ import (
|
||||
//
|
||||
// `options` are the export options applied to the exported layer.
|
||||
func ExportLayer(ctx context.Context, layerPath, exportFolderPath string, layerData LayerData, options ExportLayerOptions) (err error) {
|
||||
title := "hcsshim::ExportLayer"
|
||||
ctx, span := oc.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
title := "hcsshim.ExportLayer"
|
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End()
|
||||
defer func() { oc.SetSpanStatus(span, err) }()
|
||||
span.AddAttributes(
|
||||
@@ -30,17 +28,17 @@ func ExportLayer(ctx context.Context, layerPath, exportFolderPath string, layerD
|
||||
trace.StringAttribute("exportFolderPath", exportFolderPath),
|
||||
)
|
||||
|
||||
ldBytes, err := json.Marshal(layerData)
|
||||
ldbytes, err := json.Marshal(layerData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oBytes, err := json.Marshal(options)
|
||||
obytes, err := json.Marshal(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = hcsExportLayer(layerPath, exportFolderPath, string(ldBytes), string(oBytes))
|
||||
err = hcsExportLayer(layerPath, exportFolderPath, string(ldbytes), string(obytes))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to export layer")
|
||||
}
|
||||
|
||||
12
vendor/github.com/Microsoft/hcsshim/computestorage/format.go
generated
vendored
12
vendor/github.com/Microsoft/hcsshim/computestorage/format.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package computestorage
|
||||
|
||||
import (
|
||||
@@ -7,20 +5,16 @@ import (
|
||||
|
||||
"github.com/Microsoft/hcsshim/internal/oc"
|
||||
"github.com/pkg/errors"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// FormatWritableLayerVhd formats a virtual disk for use as a writable container layer.
|
||||
//
|
||||
// If the VHD is not mounted it will be temporarily mounted.
|
||||
//
|
||||
// NOTE: This API had a breaking change in the operating system after Windows Server 2019.
|
||||
// On ws2019 the API expects to get passed a file handle from CreateFile for the vhd that
|
||||
// the caller wants to format. On > ws2019, its expected that the caller passes a vhd handle
|
||||
// that can be obtained from the virtdisk APIs.
|
||||
func FormatWritableLayerVhd(ctx context.Context, vhdHandle windows.Handle) (err error) {
|
||||
title := "hcsshim::FormatWritableLayerVhd"
|
||||
ctx, span := oc.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
title := "hcsshim.FormatWritableLayerVhd"
|
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End()
|
||||
defer func() { oc.SetSpanStatus(span, err) }()
|
||||
|
||||
|
||||
14
vendor/github.com/Microsoft/hcsshim/computestorage/helpers.go
generated
vendored
14
vendor/github.com/Microsoft/hcsshim/computestorage/helpers.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package computestorage
|
||||
|
||||
import (
|
||||
@@ -8,12 +6,10 @@ import (
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/Microsoft/go-winio/pkg/security"
|
||||
"github.com/Microsoft/go-winio/vhd"
|
||||
"github.com/Microsoft/hcsshim/internal/memory"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/Microsoft/hcsshim/internal/security"
|
||||
)
|
||||
|
||||
const defaultVHDXBlockSizeInMB = 1
|
||||
@@ -63,8 +59,8 @@ func SetupContainerBaseLayer(ctx context.Context, layerPath, baseVhdPath, diffVh
|
||||
createParams := &vhd.CreateVirtualDiskParameters{
|
||||
Version: 2,
|
||||
Version2: vhd.CreateVersion2{
|
||||
MaximumSize: sizeInGB * memory.GiB,
|
||||
BlockSizeInBytes: defaultVHDXBlockSizeInMB * memory.MiB,
|
||||
MaximumSize: sizeInGB * 1024 * 1024 * 1024,
|
||||
BlockSizeInBytes: defaultVHDXBlockSizeInMB * 1024 * 1024,
|
||||
},
|
||||
}
|
||||
handle, err := vhd.CreateVirtualDisk(baseVhdPath, vhd.VirtualDiskAccessNone, vhd.CreateVirtualDiskFlagNone, createParams)
|
||||
@@ -139,8 +135,8 @@ func SetupUtilityVMBaseLayer(ctx context.Context, uvmPath, baseVhdPath, diffVhdP
|
||||
createParams := &vhd.CreateVirtualDiskParameters{
|
||||
Version: 2,
|
||||
Version2: vhd.CreateVersion2{
|
||||
MaximumSize: sizeInGB * memory.GiB,
|
||||
BlockSizeInBytes: defaultVHDXBlockSizeInMB * memory.MiB,
|
||||
MaximumSize: sizeInGB * 1024 * 1024 * 1024,
|
||||
BlockSizeInBytes: defaultVHDXBlockSizeInMB * 1024 * 1024,
|
||||
},
|
||||
}
|
||||
handle, err := vhd.CreateVirtualDisk(baseVhdPath, vhd.VirtualDiskAccessNone, vhd.CreateVirtualDiskFlagNone, createParams)
|
||||
|
||||
6
vendor/github.com/Microsoft/hcsshim/computestorage/import.go
generated
vendored
6
vendor/github.com/Microsoft/hcsshim/computestorage/import.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package computestorage
|
||||
|
||||
import (
|
||||
@@ -21,8 +19,8 @@ import (
|
||||
//
|
||||
// `layerData` is the parent layer data.
|
||||
func ImportLayer(ctx context.Context, layerPath, sourceFolderPath string, layerData LayerData) (err error) {
|
||||
title := "hcsshim::ImportLayer"
|
||||
ctx, span := oc.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
title := "hcsshim.ImportLayer"
|
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End()
|
||||
defer func() { oc.SetSpanStatus(span, err) }()
|
||||
span.AddAttributes(
|
||||
|
||||
6
vendor/github.com/Microsoft/hcsshim/computestorage/initialize.go
generated
vendored
6
vendor/github.com/Microsoft/hcsshim/computestorage/initialize.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package computestorage
|
||||
|
||||
import (
|
||||
@@ -18,8 +16,8 @@ import (
|
||||
//
|
||||
// `layerData` is the parent read-only layer data.
|
||||
func InitializeWritableLayer(ctx context.Context, layerPath string, layerData LayerData) (err error) {
|
||||
title := "hcsshim::InitializeWritableLayer"
|
||||
ctx, span := oc.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
title := "hcsshim.InitializeWritableLayer"
|
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End()
|
||||
defer func() { oc.SetSpanStatus(span, err) }()
|
||||
span.AddAttributes(
|
||||
|
||||
7
vendor/github.com/Microsoft/hcsshim/computestorage/mount.go
generated
vendored
7
vendor/github.com/Microsoft/hcsshim/computestorage/mount.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package computestorage
|
||||
|
||||
import (
|
||||
@@ -8,13 +6,14 @@ import (
|
||||
"github.com/Microsoft/hcsshim/internal/interop"
|
||||
"github.com/Microsoft/hcsshim/internal/oc"
|
||||
"github.com/pkg/errors"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// GetLayerVhdMountPath returns the volume path for a virtual disk of a writable container layer.
|
||||
func GetLayerVhdMountPath(ctx context.Context, vhdHandle windows.Handle) (path string, err error) {
|
||||
title := "hcsshim::GetLayerVhdMountPath"
|
||||
ctx, span := oc.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
title := "hcsshim.GetLayerVhdMountPath"
|
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End()
|
||||
defer func() { oc.SetSpanStatus(span, err) }()
|
||||
|
||||
|
||||
14
vendor/github.com/Microsoft/hcsshim/computestorage/setup.go
generated
vendored
14
vendor/github.com/Microsoft/hcsshim/computestorage/setup.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package computestorage
|
||||
|
||||
import (
|
||||
@@ -23,8 +21,8 @@ import (
|
||||
//
|
||||
// `options` are the options applied while processing the layer.
|
||||
func SetupBaseOSLayer(ctx context.Context, layerPath string, vhdHandle windows.Handle, options OsLayerOptions) (err error) {
|
||||
title := "hcsshim::SetupBaseOSLayer"
|
||||
ctx, span := oc.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
title := "hcsshim.SetupBaseOSLayer"
|
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End()
|
||||
defer func() { oc.SetSpanStatus(span, err) }()
|
||||
span.AddAttributes(
|
||||
@@ -50,16 +48,12 @@ func SetupBaseOSLayer(ctx context.Context, layerPath string, vhdHandle windows.H
|
||||
// `volumePath` is the path to the volume to be used for setup.
|
||||
//
|
||||
// `options` are the options applied while processing the layer.
|
||||
//
|
||||
// NOTE: This API is only available on builds of Windows greater than 19645. Inside we
|
||||
// check if the hosts build has the API available by using 'GetVersion' which requires
|
||||
// the calling application to be manifested. https://docs.microsoft.com/en-us/windows/win32/sbscs/manifests
|
||||
func SetupBaseOSVolume(ctx context.Context, layerPath, volumePath string, options OsLayerOptions) (err error) {
|
||||
if osversion.Build() < 19645 {
|
||||
return errors.New("SetupBaseOSVolume is not present on builds older than 19645")
|
||||
}
|
||||
title := "hcsshim::SetupBaseOSVolume"
|
||||
ctx, span := oc.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
title := "hcsshim.SetupBaseOSVolume"
|
||||
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
|
||||
defer span.End()
|
||||
defer func() { oc.SetSpanStatus(span, err) }()
|
||||
span.AddAttributes(
|
||||
|
||||
9
vendor/github.com/Microsoft/hcsshim/computestorage/storage.go
generated
vendored
9
vendor/github.com/Microsoft/hcsshim/computestorage/storage.go
generated
vendored
@@ -7,7 +7,7 @@ import (
|
||||
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go storage.go
|
||||
//go:generate go run ../mksyscall_windows.go -output zsyscall_windows.go storage.go
|
||||
|
||||
//sys hcsImportLayer(layerPath string, sourceFolderPath string, layerData string) (hr error) = computestorage.HcsImportLayer?
|
||||
//sys hcsExportLayer(layerPath string, exportFolderPath string, layerData string, options string) (hr error) = computestorage.HcsExportLayer?
|
||||
@@ -20,13 +20,10 @@ import (
|
||||
//sys hcsGetLayerVhdMountPath(vhdHandle windows.Handle, mountPath **uint16) (hr error) = computestorage.HcsGetLayerVhdMountPath?
|
||||
//sys hcsSetupBaseOSVolume(layerPath string, volumePath string, options string) (hr error) = computestorage.HcsSetupBaseOSVolume?
|
||||
|
||||
type Version = hcsschema.Version
|
||||
type Layer = hcsschema.Layer
|
||||
|
||||
// LayerData is the data used to describe parent layer information.
|
||||
type LayerData struct {
|
||||
SchemaVersion Version `json:"SchemaVersion,omitempty"`
|
||||
Layers []Layer `json:"Layers,omitempty"`
|
||||
SchemaVersion hcsschema.Version `json:"SchemaVersion,omitempty"`
|
||||
Layers []hcsschema.Layer `json:"Layers,omitempty"`
|
||||
}
|
||||
|
||||
// ExportLayerOptions are the set of options that are used with the `computestorage.HcsExportLayer` syscall.
|
||||
|
||||
203
vendor/github.com/Microsoft/hcsshim/computestorage/zsyscall_windows.go
generated
vendored
203
vendor/github.com/Microsoft/hcsshim/computestorage/zsyscall_windows.go
generated
vendored
@@ -1,6 +1,4 @@
|
||||
//go:build windows
|
||||
|
||||
// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
|
||||
// Code generated mksyscall_windows.exe DO NOT EDIT
|
||||
|
||||
package computestorage
|
||||
|
||||
@@ -21,7 +19,6 @@ const (
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
@@ -29,7 +26,7 @@ var (
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return errERROR_EINVAL
|
||||
return nil
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
@@ -42,86 +39,42 @@ func errnoErr(e syscall.Errno) error {
|
||||
var (
|
||||
modcomputestorage = windows.NewLazySystemDLL("computestorage.dll")
|
||||
|
||||
procHcsAttachLayerStorageFilter = modcomputestorage.NewProc("HcsAttachLayerStorageFilter")
|
||||
procHcsDestoryLayer = modcomputestorage.NewProc("HcsDestoryLayer")
|
||||
procHcsDetachLayerStorageFilter = modcomputestorage.NewProc("HcsDetachLayerStorageFilter")
|
||||
procHcsImportLayer = modcomputestorage.NewProc("HcsImportLayer")
|
||||
procHcsExportLayer = modcomputestorage.NewProc("HcsExportLayer")
|
||||
procHcsDestoryLayer = modcomputestorage.NewProc("HcsDestoryLayer")
|
||||
procHcsSetupBaseOSLayer = modcomputestorage.NewProc("HcsSetupBaseOSLayer")
|
||||
procHcsInitializeWritableLayer = modcomputestorage.NewProc("HcsInitializeWritableLayer")
|
||||
procHcsAttachLayerStorageFilter = modcomputestorage.NewProc("HcsAttachLayerStorageFilter")
|
||||
procHcsDetachLayerStorageFilter = modcomputestorage.NewProc("HcsDetachLayerStorageFilter")
|
||||
procHcsFormatWritableLayerVhd = modcomputestorage.NewProc("HcsFormatWritableLayerVhd")
|
||||
procHcsGetLayerVhdMountPath = modcomputestorage.NewProc("HcsGetLayerVhdMountPath")
|
||||
procHcsImportLayer = modcomputestorage.NewProc("HcsImportLayer")
|
||||
procHcsInitializeWritableLayer = modcomputestorage.NewProc("HcsInitializeWritableLayer")
|
||||
procHcsSetupBaseOSLayer = modcomputestorage.NewProc("HcsSetupBaseOSLayer")
|
||||
procHcsSetupBaseOSVolume = modcomputestorage.NewProc("HcsSetupBaseOSVolume")
|
||||
)
|
||||
|
||||
func hcsAttachLayerStorageFilter(layerPath string, layerData string) (hr error) {
|
||||
func hcsImportLayer(layerPath string, sourceFolderPath string, layerData string) (hr error) {
|
||||
var _p0 *uint16
|
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath)
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *uint16
|
||||
_p1, hr = syscall.UTF16PtrFromString(layerData)
|
||||
_p1, hr = syscall.UTF16PtrFromString(sourceFolderPath)
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
return _hcsAttachLayerStorageFilter(_p0, _p1)
|
||||
var _p2 *uint16
|
||||
_p2, hr = syscall.UTF16PtrFromString(layerData)
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
return _hcsImportLayer(_p0, _p1, _p2)
|
||||
}
|
||||
|
||||
func _hcsAttachLayerStorageFilter(layerPath *uint16, layerData *uint16) (hr error) {
|
||||
hr = procHcsAttachLayerStorageFilter.Find()
|
||||
if hr != nil {
|
||||
func _hcsImportLayer(layerPath *uint16, sourceFolderPath *uint16, layerData *uint16) (hr error) {
|
||||
if hr = procHcsImportLayer.Find(); hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsAttachLayerStorageFilter.Addr(), 2, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(layerData)), 0)
|
||||
if int32(r0) < 0 {
|
||||
if r0&0x1fff0000 == 0x00070000 {
|
||||
r0 &= 0xffff
|
||||
}
|
||||
hr = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func hcsDestroyLayer(layerPath string) (hr error) {
|
||||
var _p0 *uint16
|
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath)
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
return _hcsDestroyLayer(_p0)
|
||||
}
|
||||
|
||||
func _hcsDestroyLayer(layerPath *uint16) (hr error) {
|
||||
hr = procHcsDestoryLayer.Find()
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsDestoryLayer.Addr(), 1, uintptr(unsafe.Pointer(layerPath)), 0, 0)
|
||||
if int32(r0) < 0 {
|
||||
if r0&0x1fff0000 == 0x00070000 {
|
||||
r0 &= 0xffff
|
||||
}
|
||||
hr = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func hcsDetachLayerStorageFilter(layerPath string) (hr error) {
|
||||
var _p0 *uint16
|
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath)
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
return _hcsDetachLayerStorageFilter(_p0)
|
||||
}
|
||||
|
||||
func _hcsDetachLayerStorageFilter(layerPath *uint16) (hr error) {
|
||||
hr = procHcsDetachLayerStorageFilter.Find()
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsDetachLayerStorageFilter.Addr(), 1, uintptr(unsafe.Pointer(layerPath)), 0, 0)
|
||||
r0, _, _ := syscall.Syscall(procHcsImportLayer.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(sourceFolderPath)), uintptr(unsafe.Pointer(layerData)))
|
||||
if int32(r0) < 0 {
|
||||
if r0&0x1fff0000 == 0x00070000 {
|
||||
r0 &= 0xffff
|
||||
@@ -156,8 +109,7 @@ func hcsExportLayer(layerPath string, exportFolderPath string, layerData string,
|
||||
}
|
||||
|
||||
func _hcsExportLayer(layerPath *uint16, exportFolderPath *uint16, layerData *uint16, options *uint16) (hr error) {
|
||||
hr = procHcsExportLayer.Find()
|
||||
if hr != nil {
|
||||
if hr = procHcsExportLayer.Find(); hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall6(procHcsExportLayer.Addr(), 4, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(exportFolderPath)), uintptr(unsafe.Pointer(layerData)), uintptr(unsafe.Pointer(options)), 0, 0)
|
||||
@@ -170,12 +122,20 @@ func _hcsExportLayer(layerPath *uint16, exportFolderPath *uint16, layerData *uin
|
||||
return
|
||||
}
|
||||
|
||||
func hcsFormatWritableLayerVhd(handle windows.Handle) (hr error) {
|
||||
hr = procHcsFormatWritableLayerVhd.Find()
|
||||
func hcsDestroyLayer(layerPath string) (hr error) {
|
||||
var _p0 *uint16
|
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath)
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsFormatWritableLayerVhd.Addr(), 1, uintptr(handle), 0, 0)
|
||||
return _hcsDestroyLayer(_p0)
|
||||
}
|
||||
|
||||
func _hcsDestroyLayer(layerPath *uint16) (hr error) {
|
||||
if hr = procHcsDestoryLayer.Find(); hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsDestoryLayer.Addr(), 1, uintptr(unsafe.Pointer(layerPath)), 0, 0)
|
||||
if int32(r0) < 0 {
|
||||
if r0&0x1fff0000 == 0x00070000 {
|
||||
r0 &= 0xffff
|
||||
@@ -185,46 +145,25 @@ func hcsFormatWritableLayerVhd(handle windows.Handle) (hr error) {
|
||||
return
|
||||
}
|
||||
|
||||
func hcsGetLayerVhdMountPath(vhdHandle windows.Handle, mountPath **uint16) (hr error) {
|
||||
hr = procHcsGetLayerVhdMountPath.Find()
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsGetLayerVhdMountPath.Addr(), 2, uintptr(vhdHandle), uintptr(unsafe.Pointer(mountPath)), 0)
|
||||
if int32(r0) < 0 {
|
||||
if r0&0x1fff0000 == 0x00070000 {
|
||||
r0 &= 0xffff
|
||||
}
|
||||
hr = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func hcsImportLayer(layerPath string, sourceFolderPath string, layerData string) (hr error) {
|
||||
func hcsSetupBaseOSLayer(layerPath string, handle windows.Handle, options string) (hr error) {
|
||||
var _p0 *uint16
|
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath)
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *uint16
|
||||
_p1, hr = syscall.UTF16PtrFromString(sourceFolderPath)
|
||||
_p1, hr = syscall.UTF16PtrFromString(options)
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
var _p2 *uint16
|
||||
_p2, hr = syscall.UTF16PtrFromString(layerData)
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
return _hcsImportLayer(_p0, _p1, _p2)
|
||||
return _hcsSetupBaseOSLayer(_p0, handle, _p1)
|
||||
}
|
||||
|
||||
func _hcsImportLayer(layerPath *uint16, sourceFolderPath *uint16, layerData *uint16) (hr error) {
|
||||
hr = procHcsImportLayer.Find()
|
||||
if hr != nil {
|
||||
func _hcsSetupBaseOSLayer(layerPath *uint16, handle windows.Handle, options *uint16) (hr error) {
|
||||
if hr = procHcsSetupBaseOSLayer.Find(); hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsImportLayer.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(sourceFolderPath)), uintptr(unsafe.Pointer(layerData)))
|
||||
r0, _, _ := syscall.Syscall(procHcsSetupBaseOSLayer.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(handle), uintptr(unsafe.Pointer(options)))
|
||||
if int32(r0) < 0 {
|
||||
if r0&0x1fff0000 == 0x00070000 {
|
||||
r0 &= 0xffff
|
||||
@@ -254,8 +193,7 @@ func hcsInitializeWritableLayer(writableLayerPath string, layerData string, opti
|
||||
}
|
||||
|
||||
func _hcsInitializeWritableLayer(writableLayerPath *uint16, layerData *uint16, options *uint16) (hr error) {
|
||||
hr = procHcsInitializeWritableLayer.Find()
|
||||
if hr != nil {
|
||||
if hr = procHcsInitializeWritableLayer.Find(); hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsInitializeWritableLayer.Addr(), 3, uintptr(unsafe.Pointer(writableLayerPath)), uintptr(unsafe.Pointer(layerData)), uintptr(unsafe.Pointer(options)))
|
||||
@@ -268,26 +206,76 @@ func _hcsInitializeWritableLayer(writableLayerPath *uint16, layerData *uint16, o
|
||||
return
|
||||
}
|
||||
|
||||
func hcsSetupBaseOSLayer(layerPath string, handle windows.Handle, options string) (hr error) {
|
||||
func hcsAttachLayerStorageFilter(layerPath string, layerData string) (hr error) {
|
||||
var _p0 *uint16
|
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath)
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *uint16
|
||||
_p1, hr = syscall.UTF16PtrFromString(options)
|
||||
_p1, hr = syscall.UTF16PtrFromString(layerData)
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
return _hcsSetupBaseOSLayer(_p0, handle, _p1)
|
||||
return _hcsAttachLayerStorageFilter(_p0, _p1)
|
||||
}
|
||||
|
||||
func _hcsSetupBaseOSLayer(layerPath *uint16, handle windows.Handle, options *uint16) (hr error) {
|
||||
hr = procHcsSetupBaseOSLayer.Find()
|
||||
func _hcsAttachLayerStorageFilter(layerPath *uint16, layerData *uint16) (hr error) {
|
||||
if hr = procHcsAttachLayerStorageFilter.Find(); hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsAttachLayerStorageFilter.Addr(), 2, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(layerData)), 0)
|
||||
if int32(r0) < 0 {
|
||||
if r0&0x1fff0000 == 0x00070000 {
|
||||
r0 &= 0xffff
|
||||
}
|
||||
hr = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func hcsDetachLayerStorageFilter(layerPath string) (hr error) {
|
||||
var _p0 *uint16
|
||||
_p0, hr = syscall.UTF16PtrFromString(layerPath)
|
||||
if hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsSetupBaseOSLayer.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(handle), uintptr(unsafe.Pointer(options)))
|
||||
return _hcsDetachLayerStorageFilter(_p0)
|
||||
}
|
||||
|
||||
func _hcsDetachLayerStorageFilter(layerPath *uint16) (hr error) {
|
||||
if hr = procHcsDetachLayerStorageFilter.Find(); hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsDetachLayerStorageFilter.Addr(), 1, uintptr(unsafe.Pointer(layerPath)), 0, 0)
|
||||
if int32(r0) < 0 {
|
||||
if r0&0x1fff0000 == 0x00070000 {
|
||||
r0 &= 0xffff
|
||||
}
|
||||
hr = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func hcsFormatWritableLayerVhd(handle windows.Handle) (hr error) {
|
||||
if hr = procHcsFormatWritableLayerVhd.Find(); hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsFormatWritableLayerVhd.Addr(), 1, uintptr(handle), 0, 0)
|
||||
if int32(r0) < 0 {
|
||||
if r0&0x1fff0000 == 0x00070000 {
|
||||
r0 &= 0xffff
|
||||
}
|
||||
hr = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func hcsGetLayerVhdMountPath(vhdHandle windows.Handle, mountPath **uint16) (hr error) {
|
||||
if hr = procHcsGetLayerVhdMountPath.Find(); hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsGetLayerVhdMountPath.Addr(), 2, uintptr(vhdHandle), uintptr(unsafe.Pointer(mountPath)), 0)
|
||||
if int32(r0) < 0 {
|
||||
if r0&0x1fff0000 == 0x00070000 {
|
||||
r0 &= 0xffff
|
||||
@@ -317,8 +305,7 @@ func hcsSetupBaseOSVolume(layerPath string, volumePath string, options string) (
|
||||
}
|
||||
|
||||
func _hcsSetupBaseOSVolume(layerPath *uint16, volumePath *uint16, options *uint16) (hr error) {
|
||||
hr = procHcsSetupBaseOSVolume.Find()
|
||||
if hr != nil {
|
||||
if hr = procHcsSetupBaseOSVolume.Find(); hr != nil {
|
||||
return
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procHcsSetupBaseOSVolume.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(volumePath)), uintptr(unsafe.Pointer(options)))
|
||||
|
||||
4
vendor/github.com/Microsoft/hcsshim/container.go
generated
vendored
4
vendor/github.com/Microsoft/hcsshim/container.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package hcsshim
|
||||
|
||||
import (
|
||||
@@ -62,7 +60,7 @@ type container struct {
|
||||
waitCh chan struct{}
|
||||
}
|
||||
|
||||
// createContainerAdditionalJSON is read from the environment at initialization
|
||||
// createComputeSystemAdditionalJSON is read from the environment at initialisation
|
||||
// time. It allows an environment variable to define additional JSON which
|
||||
// is merged in the CreateComputeSystem call to HCS.
|
||||
var createContainerAdditionalJSON []byte
|
||||
|
||||
5
vendor/github.com/Microsoft/hcsshim/errors.go
generated
vendored
5
vendor/github.com/Microsoft/hcsshim/errors.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package hcsshim
|
||||
|
||||
import (
|
||||
@@ -52,9 +50,6 @@ var (
|
||||
// ErrUnexpectedValue is an error encountered when hcs returns an invalid value
|
||||
ErrUnexpectedValue = hcs.ErrUnexpectedValue
|
||||
|
||||
// ErrOperationDenied is an error when hcs attempts an operation that is explicitly denied
|
||||
ErrOperationDenied = hcs.ErrOperationDenied
|
||||
|
||||
// ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container
|
||||
ErrVmcomputeAlreadyStopped = hcs.ErrVmcomputeAlreadyStopped
|
||||
|
||||
|
||||
12
vendor/github.com/Microsoft/hcsshim/functional_tests.ps1
generated
vendored
Normal file
12
vendor/github.com/Microsoft/hcsshim/functional_tests.ps1
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# Requirements so far:
|
||||
# dockerd running
|
||||
# - image microsoft/nanoserver (matching host base image) docker load -i c:\baseimages\nanoserver.tar
|
||||
# - image alpine (linux) docker pull --platform=linux alpine
|
||||
|
||||
|
||||
# TODO: Add this a parameter for debugging. ie "functional-tests -debug=$true"
|
||||
#$env:HCSSHIM_FUNCTIONAL_TESTS_DEBUG="yes please"
|
||||
|
||||
#pushd uvm
|
||||
go test -v -tags "functional uvmcreate uvmscratch uvmscsi uvmvpmem uvmvsmb uvmp9" ./...
|
||||
#popd
|
||||
12
vendor/github.com/Microsoft/hcsshim/hcsshim.go
generated
vendored
12
vendor/github.com/Microsoft/hcsshim/hcsshim.go
generated
vendored
@@ -1,17 +1,15 @@
|
||||
//go:build windows
|
||||
|
||||
// Shim for the Host Compute Service (HCS) to manage Windows Server
|
||||
// containers and Hyper-V containers.
|
||||
|
||||
package hcsshim
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"syscall"
|
||||
|
||||
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go hcsshim.go
|
||||
//go:generate go run mksyscall_windows.go -output zsyscall_windows.go hcsshim.go
|
||||
|
||||
//sys SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) = iphlpapi.SetCurrentThreadCompartmentId
|
||||
|
||||
@@ -19,9 +17,9 @@ const (
|
||||
// Specific user-visible exit codes
|
||||
WaitErrExecFailed = 32767
|
||||
|
||||
ERROR_GEN_FAILURE = windows.ERROR_GEN_FAILURE
|
||||
ERROR_SHUTDOWN_IN_PROGRESS = windows.ERROR_SHUTDOWN_IN_PROGRESS
|
||||
WSAEINVAL = windows.WSAEINVAL
|
||||
ERROR_GEN_FAILURE = hcserror.ERROR_GEN_FAILURE
|
||||
ERROR_SHUTDOWN_IN_PROGRESS = syscall.Errno(1115)
|
||||
WSAEINVAL = syscall.Errno(10022)
|
||||
|
||||
// Timeout on wait calls
|
||||
TimeoutInfinite = 0xFFFFFFFF
|
||||
|
||||
4
vendor/github.com/Microsoft/hcsshim/hnsendpoint.go
generated
vendored
4
vendor/github.com/Microsoft/hcsshim/hnsendpoint.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package hcsshim
|
||||
|
||||
import (
|
||||
@@ -15,7 +13,7 @@ type HNSEndpointStats = hns.EndpointStats
|
||||
// Namespace represents a Compartment.
|
||||
type Namespace = hns.Namespace
|
||||
|
||||
// SystemType represents the type of the system on which actions are done
|
||||
//SystemType represents the type of the system on which actions are done
|
||||
type SystemType string
|
||||
|
||||
// SystemType const
|
||||
|
||||
2
vendor/github.com/Microsoft/hcsshim/hnsglobals.go
generated
vendored
2
vendor/github.com/Microsoft/hcsshim/hnsglobals.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package hcsshim
|
||||
|
||||
import (
|
||||
|
||||
6
vendor/github.com/Microsoft/hcsshim/hnsnetwork.go
generated
vendored
6
vendor/github.com/Microsoft/hcsshim/hnsnetwork.go
generated
vendored
@@ -1,16 +1,14 @@
|
||||
//go:build windows
|
||||
|
||||
package hcsshim
|
||||
|
||||
import (
|
||||
"github.com/Microsoft/hcsshim/internal/hns"
|
||||
)
|
||||
|
||||
// Subnet is associated with a network and represents a list
|
||||
// Subnet is assoicated with a network and represents a list
|
||||
// of subnets available to the network
|
||||
type Subnet = hns.Subnet
|
||||
|
||||
// MacPool is associated with a network and represents a list
|
||||
// MacPool is assoicated with a network and represents a list
|
||||
// of macaddresses available to the network
|
||||
type MacPool = hns.MacPool
|
||||
|
||||
|
||||
2
vendor/github.com/Microsoft/hcsshim/hnspolicylist.go
generated
vendored
2
vendor/github.com/Microsoft/hcsshim/hnspolicylist.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package hcsshim
|
||||
|
||||
import (
|
||||
|
||||
2
vendor/github.com/Microsoft/hcsshim/hnssupport.go
generated
vendored
2
vendor/github.com/Microsoft/hcsshim/hnssupport.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package hcsshim
|
||||
|
||||
import (
|
||||
|
||||
2
vendor/github.com/Microsoft/hcsshim/interface.go
generated
vendored
2
vendor/github.com/Microsoft/hcsshim/interface.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package hcsshim
|
||||
|
||||
import (
|
||||
|
||||
2
vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go
generated
vendored
2
vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package cow
|
||||
|
||||
import (
|
||||
|
||||
2
vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go
generated
vendored
2
vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
//go:build windows
|
||||
|
||||
package hcs
|
||||
|
||||
import (
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user