mirror of
https://github.com/containers/skopeo.git
synced 2025-08-11 11:22:05 +00:00
Merge pull request #920 from zhangguanzhang/master
Add tags to support regular expressions without breaking the old ones in yaml conf
This commit is contained in:
commit
2afe7a3e1e
@ -8,6 +8,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/image/v5/copy"
|
"github.com/containers/image/v5/copy"
|
||||||
@ -50,7 +51,7 @@ type tlsVerifyConfig struct {
|
|||||||
// registrySyncConfig contains information about a single registry, read from
|
// registrySyncConfig contains information about a single registry, read from
|
||||||
// the source YAML file
|
// the source YAML file
|
||||||
type registrySyncConfig struct {
|
type registrySyncConfig struct {
|
||||||
Images map[string][]string // Images map images name to slices with the images' tags
|
Images map[string]interface{} // Images map images name to slices or regular expression with the images' tags
|
||||||
Credentials types.DockerAuthConfig // Username and password used to authenticate with the registry
|
Credentials types.DockerAuthConfig // Username and password used to authenticate with the registry
|
||||||
TLSVerify tlsVerifyConfig `yaml:"tls-verify"` // TLS verification mode (enabled by default)
|
TLSVerify tlsVerifyConfig `yaml:"tls-verify"` // TLS verification mode (enabled by default)
|
||||||
CertDir string `yaml:"cert-dir"` // Path to the TLS certificates of the registry
|
CertDir string `yaml:"cert-dir"` // Path to the TLS certificates of the registry
|
||||||
@ -278,7 +279,32 @@ func imagesToCopyFromRegistry(registryName string, cfg registrySyncConfig, sourc
|
|||||||
serverCtx.DockerAuthConfig = &cfg.Credentials
|
serverCtx.DockerAuthConfig = &cfg.Credentials
|
||||||
|
|
||||||
var sourceReferences []types.ImageReference
|
var sourceReferences []types.ImageReference
|
||||||
for _, tag := range tags {
|
|
||||||
|
switch tags.(type) {
|
||||||
|
case []string, []interface{}, nil:
|
||||||
|
tagList := make([]string, 0)
|
||||||
|
if tagIns, ok := tags.([]interface{}); ok {
|
||||||
|
for _, tagValue := range tagIns {
|
||||||
|
switch tagValue.(type) {
|
||||||
|
case string, int, float64:
|
||||||
|
tagList = append(tagList, fmt.Sprintf("%v", tagValue))
|
||||||
|
default:
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"repo": imageName,
|
||||||
|
"registry": registryName,
|
||||||
|
}).Error("Error processing repo, skipping")
|
||||||
|
logrus.Errorf("Elements can only be strings if they are of type array, wrong value (%v|%T)", tagValue, tagValue)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// nil is equl full tags
|
||||||
|
if tags != nil {
|
||||||
|
tagList = tags.([]string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range tagList {
|
||||||
source := fmt.Sprintf("%s:%s", repoName, tag)
|
source := fmt.Sprintf("%s:%s", repoName, tag)
|
||||||
|
|
||||||
imageRef, err := docker.ParseReference(source)
|
imageRef, err := docker.ParseReference(source)
|
||||||
@ -292,7 +318,7 @@ func imagesToCopyFromRegistry(registryName string, cfg registrySyncConfig, sourc
|
|||||||
sourceReferences = append(sourceReferences, imageRef)
|
sourceReferences = append(sourceReferences, imageRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(tags) == 0 {
|
if len(tagList) == 0 {
|
||||||
logrus.WithFields(logrus.Fields{
|
logrus.WithFields(logrus.Fields{
|
||||||
"repo": imageName,
|
"repo": imageName,
|
||||||
"registry": registryName,
|
"registry": registryName,
|
||||||
@ -319,6 +345,61 @@ func imagesToCopyFromRegistry(registryName string, cfg registrySyncConfig, sourc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case string:
|
||||||
|
tagReg, err := regexp.Compile(tags.(string))
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"repo": imageName,
|
||||||
|
"registry": registryName,
|
||||||
|
}).Error("Error processing repo, skipping")
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"repo": imageName,
|
||||||
|
"registry": registryName,
|
||||||
|
}).Info("Querying registry for image tags")
|
||||||
|
|
||||||
|
imageRef, err := docker.ParseReference(repoName)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"repo": imageName,
|
||||||
|
"registry": registryName,
|
||||||
|
}).Error("Error processing repo, skipping")
|
||||||
|
logrus.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
allSourceReferences, err := imagesToCopyFromRepo(imageRef, repoName, serverCtx)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"repo": imageName,
|
||||||
|
"registry": registryName,
|
||||||
|
}).Error("Error processing repo, skipping")
|
||||||
|
logrus.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"repo": imageName,
|
||||||
|
"registry": registryName,
|
||||||
|
}).Infof("Start filtering using the regular expression: %v", tags.(string))
|
||||||
|
for _, sReference := range allSourceReferences {
|
||||||
|
// get the tag names to match, [1] default is "latest" by .DockerReference().String()
|
||||||
|
if tagReg.Match([]byte(strings.Split(sReference.DockerReference().String(), ":")[1])) {
|
||||||
|
sourceReferences = append(sourceReferences, sReference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"repo": imageName,
|
||||||
|
"registry": registryName,
|
||||||
|
}).Error("Error processing repo, skipping")
|
||||||
|
logrus.Errorf("Tags's type only support []string or regular expression string, wrong type:(%v %T)", tags, tags)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if len(sourceReferences) == 0 {
|
if len(sourceReferences) == 0 {
|
||||||
logrus.WithFields(logrus.Fields{
|
logrus.WithFields(logrus.Fields{
|
||||||
"repo": imageName,
|
"repo": imageName,
|
||||||
|
@ -86,6 +86,21 @@ Images are located at:
|
|||||||
/media/usb/busybox:latest
|
/media/usb/busybox:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Synchronizing to a container registry from local
|
||||||
|
The Image's locate info:
|
||||||
|
```
|
||||||
|
/media/usb/busybox:1-glibc/
|
||||||
|
```
|
||||||
|
Sync run
|
||||||
|
```
|
||||||
|
$ skopeo sync --src dir --dest docker /media/usb/busybox\:1-glibc my-registry.local.lan/test/
|
||||||
|
```
|
||||||
|
Destination registry content:
|
||||||
|
```
|
||||||
|
REPO TAGS
|
||||||
|
my-registry.local.lan/test/busybox 1-glibc
|
||||||
|
```
|
||||||
|
|
||||||
### Synchronizing to a local directory, scoped
|
### Synchronizing to a local directory, scoped
|
||||||
```
|
```
|
||||||
$ skopeo sync --src docker --dest dir --scoped registry.example.com/busybox /media/usb
|
$ skopeo sync --src docker --dest dir --scoped registry.example.com/busybox /media/usb
|
||||||
@ -128,6 +143,7 @@ registry.example.com:
|
|||||||
redis:
|
redis:
|
||||||
- "1.0"
|
- "1.0"
|
||||||
- "2.0"
|
- "2.0"
|
||||||
|
nginx: ^1\.13\.[12]-alpine-perl$ # String types are used for regular expressions, it will match `1.13.1-alpine-perl` and `1.13.2-alpine-perl`
|
||||||
credentials:
|
credentials:
|
||||||
username: john
|
username: john
|
||||||
password: this is a secret
|
password: this is a secret
|
||||||
@ -139,10 +155,14 @@ quay.io:
|
|||||||
coreos/etcd:
|
coreos/etcd:
|
||||||
- latest
|
- latest
|
||||||
```
|
```
|
||||||
|
If the yaml filename is `sync.yml`, sync run:
|
||||||
|
```
|
||||||
|
skopeo sync --src yaml --dest docker sync.yml my-registry.local.lan/repo/
|
||||||
|
```
|
||||||
This will copy the following images:
|
This will copy the following images:
|
||||||
- Repository `registry.example.com/busybox`: all images, as no tags are specified.
|
- Repository `registry.example.com/busybox`: all images, as no tags are specified.
|
||||||
- Repository `registry.example.com/redis`: images tagged "1.0" and "2.0".
|
- Repository `registry.example.com/redis`: images tagged "1.0" and "2.0".
|
||||||
|
- Repository `registry.example.com/nginx`: images tagged "1.13.1-alpine-perl" and "1.13.2-alpine-perl".
|
||||||
- Repository `quay.io/coreos/etcd`: images tagged "latest".
|
- Repository `quay.io/coreos/etcd`: images tagged "latest".
|
||||||
|
|
||||||
For the registry `registry.example.com`, the "john"/"this is a secret" credentials are used, with server TLS certificates located at `/home/john/certs`.
|
For the registry `registry.example.com`, the "john"/"this is a secret" credentials are used, with server TLS certificates located at `/home/john/certs`.
|
||||||
|
@ -255,6 +255,40 @@ docker.io:
|
|||||||
c.Assert(nManifests, check.Equals, len(tags))
|
c.Assert(nManifests, check.Equals, len(tags))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SyncSuite) TestYamlRegex2Dir(c *check.C) {
|
||||||
|
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
dir1 := path.Join(tmpDir, "dir1")
|
||||||
|
|
||||||
|
yamlConfig := `
|
||||||
|
docker.io:
|
||||||
|
images:
|
||||||
|
nginx: ^1\.13\.[12]-alpine-perl$ # regex string test
|
||||||
|
`
|
||||||
|
// the ↑ regex strings always matches only 2 images
|
||||||
|
var nTags = 2
|
||||||
|
c.Assert(nTags, check.Not(check.Equals), 0)
|
||||||
|
|
||||||
|
yamlFile := path.Join(tmpDir, "registries.yaml")
|
||||||
|
ioutil.WriteFile(yamlFile, []byte(yamlConfig), 0644)
|
||||||
|
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
|
||||||
|
|
||||||
|
nManifests := 0
|
||||||
|
err = filepath.Walk(dir1, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !info.IsDir() && info.Name() == "manifest.json" {
|
||||||
|
nManifests++
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
c.Assert(nManifests, check.Equals, nTags)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SyncSuite) TestYaml2Dir(c *check.C) {
|
func (s *SyncSuite) TestYaml2Dir(c *check.C) {
|
||||||
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
|
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
@ -270,7 +304,6 @@ docker.io:
|
|||||||
alpine:
|
alpine:
|
||||||
- edge
|
- edge
|
||||||
- 3.8
|
- 3.8
|
||||||
|
|
||||||
opensuse/leap:
|
opensuse/leap:
|
||||||
- latest
|
- latest
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user