mirror of
https://github.com/crazy-max/diun.git
synced 2025-12-21 13:23:09 +01:00
Move registry client to a dedicated package
This commit is contained in:
73
pkg/registry/image.go
Normal file
73
pkg/registry/image.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Source: https://github.com/genuinetools/reg/blob/f3a9b00ec86f334702381edf842f03b3a9243a0a/registry/image.go
|
||||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/image/docker/reference"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// Image holds information about an image.
|
||||
type Image struct {
|
||||
Domain string
|
||||
Path string
|
||||
Tag string
|
||||
Digest digest.Digest
|
||||
named reference.Named
|
||||
}
|
||||
|
||||
// Name returns the full name representation of an image.
|
||||
func (i Image) Name() string {
|
||||
return i.named.Name()
|
||||
}
|
||||
|
||||
// String returns the string representation of an image.
|
||||
func (i Image) String() string {
|
||||
return i.named.String()
|
||||
}
|
||||
|
||||
// Reference returns either the digest if it is non-empty or the tag for the image.
|
||||
func (i Image) Reference() string {
|
||||
if len(i.Digest.String()) > 1 {
|
||||
return i.Digest.String()
|
||||
}
|
||||
|
||||
return i.Tag
|
||||
}
|
||||
|
||||
// WithDigest sets the digest for an image.
|
||||
func (i *Image) WithDigest(digest digest.Digest) (err error) {
|
||||
i.Digest = digest
|
||||
i.named, err = reference.WithDigest(i.named, digest)
|
||||
return err
|
||||
}
|
||||
|
||||
// ParseImage returns an Image struct with all the values filled in for a given image.
|
||||
func ParseImage(image string) (Image, error) {
|
||||
// Parse the image name and tag.
|
||||
named, err := reference.ParseNormalizedNamed(image)
|
||||
if err != nil {
|
||||
return Image{}, fmt.Errorf("parsing image %q failed: %v", image, err)
|
||||
}
|
||||
// Add the latest lag if they did not provide one.
|
||||
named = reference.TagNameOnly(named)
|
||||
|
||||
i := Image{
|
||||
named: named,
|
||||
Domain: reference.Domain(named),
|
||||
Path: reference.Path(named),
|
||||
}
|
||||
|
||||
// Add the tag if there was one.
|
||||
if tagged, ok := named.(reference.Tagged); ok {
|
||||
i.Tag = tagged.Tag()
|
||||
}
|
||||
|
||||
// Add the digest if there was one.
|
||||
if canonical, ok := named.(reference.Canonical); ok {
|
||||
i.Digest = canonical.Digest()
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
66
pkg/registry/manifest.go
Normal file
66
pkg/registry/manifest.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/manifest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
type Manifest struct {
|
||||
Name string
|
||||
Tag string
|
||||
MIMEType string
|
||||
Digest digest.Digest
|
||||
Created *time.Time
|
||||
DockerVersion string
|
||||
Labels map[string]string
|
||||
Architecture string
|
||||
Os string
|
||||
Layers []string
|
||||
}
|
||||
|
||||
// Manifest returns the manifest for a specific image
|
||||
func (c *Client) Manifest(image Image) (Manifest, error) {
|
||||
ctx, cancel := c.timeoutContext()
|
||||
defer cancel()
|
||||
|
||||
imgCls, err := c.newImage(ctx, image.String())
|
||||
if err != nil {
|
||||
return Manifest{}, err
|
||||
}
|
||||
defer imgCls.Close()
|
||||
|
||||
rawManifest, _, err := imgCls.Manifest(ctx)
|
||||
if err != nil {
|
||||
return Manifest{}, err
|
||||
}
|
||||
|
||||
imgInspect, err := imgCls.Inspect(ctx)
|
||||
if err != nil {
|
||||
return Manifest{}, err
|
||||
}
|
||||
|
||||
imgDigest, err := manifest.Digest(rawManifest)
|
||||
if err != nil {
|
||||
return Manifest{}, err
|
||||
}
|
||||
|
||||
imgTag := imgInspect.Tag
|
||||
if imgTag == "" {
|
||||
imgTag = image.Tag
|
||||
}
|
||||
|
||||
return Manifest{
|
||||
Name: imgCls.Reference().DockerReference().Name(),
|
||||
Tag: imgTag,
|
||||
MIMEType: manifest.GuessMIMEType(rawManifest),
|
||||
Digest: imgDigest,
|
||||
Created: imgInspect.Created,
|
||||
DockerVersion: imgInspect.DockerVersion,
|
||||
Labels: imgInspect.Labels,
|
||||
Architecture: imgInspect.Architecture,
|
||||
Os: imgInspect.Os,
|
||||
Layers: imgInspect.Layers,
|
||||
}, nil
|
||||
}
|
||||
81
pkg/registry/registry.go
Normal file
81
pkg/registry/registry.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/docker"
|
||||
"github.com/containers/image/types"
|
||||
)
|
||||
|
||||
// Client represents an active docker registry object
|
||||
type Client struct {
|
||||
opts Options
|
||||
sysCtx *types.SystemContext
|
||||
}
|
||||
|
||||
// Options holds docker registry object options
|
||||
type Options struct {
|
||||
Os string
|
||||
Arch string
|
||||
Username string
|
||||
Password string
|
||||
InsecureTLS bool
|
||||
Timeout time.Duration
|
||||
UserAgent string
|
||||
}
|
||||
|
||||
// New creates new docker registry client instance
|
||||
func New(opts Options) (*Client, error) {
|
||||
// Auth
|
||||
auth := &types.DockerAuthConfig{}
|
||||
if opts.Username != "" {
|
||||
auth = &types.DockerAuthConfig{
|
||||
Username: opts.Username,
|
||||
Password: opts.Password,
|
||||
}
|
||||
}
|
||||
|
||||
// Sys context
|
||||
sysCtx := &types.SystemContext{
|
||||
OSChoice: opts.Os,
|
||||
ArchitectureChoice: opts.Arch,
|
||||
DockerAuthConfig: auth,
|
||||
DockerDaemonInsecureSkipTLSVerify: opts.InsecureTLS,
|
||||
DockerInsecureSkipTLSVerify: types.NewOptionalBool(opts.InsecureTLS),
|
||||
DockerRegistryUserAgent: opts.UserAgent,
|
||||
}
|
||||
|
||||
return &Client{
|
||||
sysCtx: sysCtx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) timeoutContext() (context.Context, context.CancelFunc) {
|
||||
ctx := context.Background()
|
||||
var cancel context.CancelFunc = func() {}
|
||||
if c.opts.Timeout > 0 {
|
||||
ctx, cancel = context.WithTimeout(ctx, c.opts.Timeout)
|
||||
}
|
||||
return ctx, cancel
|
||||
}
|
||||
|
||||
func (c *Client) newImage(ctx context.Context, imageStr string) (types.ImageCloser, error) {
|
||||
if !strings.HasPrefix(imageStr, "//") {
|
||||
imageStr = fmt.Sprintf("//%s", imageStr)
|
||||
}
|
||||
|
||||
ref, err := docker.ParseReference(imageStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid image name %s: %v", imageStr, err)
|
||||
}
|
||||
|
||||
img, err := ref.NewImage(ctx, c.sysCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
67
pkg/registry/tags.go
Normal file
67
pkg/registry/tags.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/containers/image/docker"
|
||||
"github.com/crazy-max/diun/pkg/utl"
|
||||
)
|
||||
|
||||
type Tags struct {
|
||||
List []string
|
||||
NotIncluded int
|
||||
Excluded int
|
||||
Total int
|
||||
}
|
||||
|
||||
type TagsOptions struct {
|
||||
Image Image
|
||||
Max int
|
||||
Include []string
|
||||
Exclude []string
|
||||
}
|
||||
|
||||
// Tags returns tags of a Docker repository
|
||||
func (c *Client) Tags(opts TagsOptions) (*Tags, error) {
|
||||
ctx, cancel := c.timeoutContext()
|
||||
defer cancel()
|
||||
|
||||
imgCls, err := c.newImage(ctx, opts.Image.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer imgCls.Close()
|
||||
|
||||
tags, err := docker.GetRepositoryTags(ctx, c.sysCtx, imgCls.Reference())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &Tags{
|
||||
NotIncluded: 0,
|
||||
Excluded: 0,
|
||||
Total: len(tags),
|
||||
}
|
||||
|
||||
// Filter
|
||||
for _, tag := range tags {
|
||||
if !utl.IsIncluded(tag, opts.Include) {
|
||||
res.NotIncluded++
|
||||
continue
|
||||
} else if utl.IsExcluded(tag, opts.Exclude) {
|
||||
res.Excluded++
|
||||
continue
|
||||
}
|
||||
res.List = append(res.List, tag)
|
||||
}
|
||||
|
||||
// Reverse order (latest tags first)
|
||||
for i := len(res.List)/2 - 1; i >= 0; i-- {
|
||||
opp := len(res.List) - 1 - i
|
||||
res.List[i], res.List[opp] = res.List[opp], res.List[i]
|
||||
}
|
||||
|
||||
if opts.Max > 0 && len(res.List) >= opts.Max {
|
||||
res.List = res.List[:opts.Max]
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
Reference in New Issue
Block a user