Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

git revision policy #15

Open
squaremo opened this issue Aug 5, 2020 · 1 comment
Open

git revision policy #15

squaremo opened this issue Aug 5, 2020 · 1 comment
Labels
blocked/requires-rfc Requires a design proposal enhancement New feature or request

Comments

@squaremo
Copy link
Member

squaremo commented Aug 5, 2020

So far there's a semver policy for selecting images, which selects the highest version within a given range.

Another variety of policy that we use everywhere in the Weave Cloud dev environment is "run the most recently built image", which is calculated by looking at the build timestamp. This is easily tripped up, though, in a few ways:

  • sometimes an image build will result in an image that already exists (e.g., if you revert a code change), which will have the old timestamp
  • getting the build timestamp relies on interpreting fields and labels in the image metadata -- there's no single timestamp field.

What's actually required is the image built from the most recent commit (.. on a particular branch). The git revision policy would calculate that by looking at the git repository itself, to determine which among the available images should be selected.

kind: ImagePolicy
spec:
  imageRepository:
    name: flux-image
  policy:
    gitRevision:
      gitRepositoryRef:
        name: flux-repo
      branch: main

NB this will probably need a bit of extra config to tell it how to get the revision of a given image -- either by parsing its tag, or by looking at a label in the metadata -- tbd.

@squaremo squaremo added the enhancement New feature or request label Aug 5, 2020
@bigkevmcd
Copy link
Contributor

bigkevmcd commented Sep 7, 2020

I've experimented a bit with a structure that looks like this:

// Identifier implementations are responsible for returning an image tag for a specific commit.
// If no image can be identified, return an empty string for the image.
type Identifier interface {
    Identify(c *object.Commit) (string, error)
}

Plugged into something like this:

func (i ImageIdentifier) FindMostRecentImage(r *git.Repository) (string, error) {
	commits, err := r.CommitObjects()
	if err != nil {
		return "", fmt.Errorf("failed to get commit objects from repository: %w", err)
	}
	foundImage := ""
	foundErr := errors.New("marker error")
	err = commits.ForEach(func(c *object.Commit) error {
		image, err := i.identifier.Identify(c)
		if err != nil {
			return err
		}
		if image != "" {
			foundImage = image
			return foundErr
		}
		return nil
	})
	if err != foundErr {
		return "", err
	}
	return foundImage, nil
}

It walks the commits from a git.Repository (from go-git) and tries to find an image corresponding to a commit.

A simple, prefix-based implementation of the Identifier interface looks like this:

type TagPrefixIdentifier struct {
	Prefix string
	tags   []string
}

func NewTagPrefixIdentifier(image, prefix string) (*TagPrefixIdentifier, error) {
	sourceRepo, err := name.NewRepository(image)
	if err != nil {
		return nil, fmt.Errorf("unable to parse image %q: %w", image, err)
	}
	tags, err := remote.List(sourceRepo)
	if err != nil {
		return nil, fmt.Errorf("unable to get tags for %q: %w", image, err)
	}
	return &TagPrefixIdentifier{Prefix: prefix, tags: tags}, nil
}

// Identify takes a commit Hash e.g. '8609b01765984664cad3615801efa550417442ff' and loops through the tags for
// an image repository, matching on the image, e.g. with image 'main-8609b01', and a prefix of 'main-' this
// would trim the 'main-' and match on the Hash (as a prefix) and return as a match.
func (t TagPrefixIdentifier) Identify(c *object.Commit) (string, error) {
	for _, tag := range t.tags {
		if strings.HasPrefix(c.Hash.String(), strings.TrimPrefix(tag, t.Prefix)) {
			return tag, nil
		}
	}
	return "", nil
}

The Identifier interface also makes it (expensive, but cacheable) to do "label" based lookups, where the Docker images are "labelled" (not tagged) with the commit SHA, and identify the image for a commit.

One of the core things here is that it's iterating over the commits, on the assumption that the "latest image" is most likely to be near the HEAD.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked/requires-rfc Requires a design proposal enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants