blob: 4d4fab5c4fe7e68accbef72eec401c4b4867fc53 [file] [log] [blame]
package child
import (
"context"
"strings"
"go.skia.org/infra/autoroll/go/config"
"go.skia.org/infra/autoroll/go/revision"
"go.skia.org/infra/go/docker"
"go.skia.org/infra/go/skerr"
"go.skia.org/infra/go/util"
"go.skia.org/infra/go/vfs"
)
// NewDocker returns an implementation of Child which deals with Docker images.
func NewDocker(ctx context.Context, c *config.DockerChildConfig) (*DockerChild, error) {
if err := c.Validate(); err != nil {
return nil, skerr.Wrap(err)
}
dockerClient, err := docker.NewClient(ctx)
if err != nil {
return nil, skerr.Wrap(err)
}
return &DockerChild{
client: dockerClient,
registry: c.Registry,
repo: c.Repository,
tag: c.Tag,
}, nil
}
// DockerChild is an implementation of Child which deals with Docker images.
type DockerChild struct {
client docker.Client
registry string
repo string
tag string
}
// GetRevision implements Child.
func (c *DockerChild) GetRevision(ctx context.Context, id string) (*revision.Revision, error) {
manifest, err := c.client.GetManifest(ctx, c.registry, c.repo, id)
if err != nil {
return nil, skerr.Wrap(err)
}
config, err := c.client.GetConfig(ctx, c.registry, c.repo, manifest.Config.Digest)
if err != nil {
return nil, skerr.Wrap(err)
}
// Use a shortened digest for display, without the "sha256:" prefix.
display := strings.TrimPrefix(manifest.Digest, "sha256:")
if len(display) > 12 {
display = display[:12]
}
// Sometimes creation timestamps are zero. I'm not sure why this is, but if
// we dig into the image history we can find some which are non-zero. The
// most recent layer(s) should be close to the correct time.
timestamp := config.Created
if util.TimeIsZero(timestamp) {
for _, hist := range config.History {
if hist.Created.After(timestamp) {
timestamp = hist.Created
}
}
}
return &revision.Revision{
Id: manifest.Digest,
Checksum: manifest.Digest,
Author: config.Author,
Display: display,
Timestamp: timestamp,
}, nil
}
// LogRevisions implements Child.
func (c *DockerChild) LogRevisions(ctx context.Context, from, to *revision.Revision) ([]*revision.Revision, error) {
var revs []*revision.Revision
if from.Id != to.Id {
revs = append(revs, to)
}
return revs, nil
}
// Update implements Child.
func (c *DockerChild) Update(ctx context.Context, lastRollRev *revision.Revision) (*revision.Revision, []*revision.Revision, error) {
tipRev, err := c.GetRevision(ctx, c.tag)
if err != nil {
return nil, nil, skerr.Wrap(err)
}
notRolledRevs, err := c.LogRevisions(ctx, lastRollRev, tipRev)
if err != nil {
return nil, nil, skerr.Wrap(err)
}
return tipRev, notRolledRevs, nil
}
// VFS implements the Child interface.
func (c *DockerChild) VFS(ctx context.Context, rev *revision.Revision) (vfs.FS, error) {
return nil, skerr.Fmt("VFS not implemented for DockerChild")
}
// Assert that DockerChild implements Child.
var _ Child = &DockerChild{}