Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Add ListImages summary fields #1084

Merged
merged 7 commits into from
May 21, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions api/v6/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,17 @@ type ControllerStatus struct {
type Container struct {
Name string
Current image.Info
Available []image.Info
AvailableError string `json:",omitempty"`
LatestFiltered image.Info

// All available images (ignoring tag filters)
Available []image.Info
AvailableError string `json:",omitempty"`
AvailableImagesCount int
NewAvailableImagesCount int

// Filtered available images (matching tag filters)
FilteredImagesCount int
NewFilteredImagesCount int
}

// --- config types
Expand Down
93 changes: 69 additions & 24 deletions daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,35 +70,43 @@ func (d *Daemon) Export(ctx context.Context) ([]byte, error) {
return d.Cluster.Export()
}

func (d *Daemon) ListServices(ctx context.Context, namespace string) ([]v6.ControllerStatus, error) {
clusterServices, err := d.Cluster.AllControllers(namespace)
if err != nil {
return nil, errors.Wrap(err, "getting services from cluster")
}

func (d *Daemon) getPolicyResourceMap(ctx context.Context) (policy.ResourceMap, v6.ReadOnlyReason, error) {
var services policy.ResourceMap
var globalReadOnly v6.ReadOnlyReason
err = d.WithClone(ctx, func(checkout *git.Checkout) error {
err := d.WithClone(ctx, func(checkout *git.Checkout) error {
var err error
services, err = d.Manifests.ServicesWithPolicies(checkout.ManifestDir())
return err
})

// Capture errors related to read-only repositories
switch {
case err == git.ErrNotReady:
globalReadOnly = v6.ReadOnlyNotReady
case err == git.ErrNoConfig:
globalReadOnly = v6.ReadOnlyNoRepo
case err != nil:
return nil, errors.Wrap(err, "getting service policies")
return nil, globalReadOnly, errors.Wrap(err, "getting service policies")
}

return services, globalReadOnly, nil
}

func (d *Daemon) ListServices(ctx context.Context, namespace string) ([]v6.ControllerStatus, error) {
clusterServices, err := d.Cluster.AllControllers(namespace)
if err != nil {
return nil, errors.Wrap(err, "getting services from cluster")
}

policyResourceMap, readOnly, err := d.getPolicyResourceMap(ctx)
if err != nil {
return nil, err
}

var res []v6.ControllerStatus
for _, service := range clusterServices {
var readOnly v6.ReadOnlyReason
policies, ok := services[service.ID]
policies, ok := policyResourceMap[service.ID]
switch {
case globalReadOnly != "":
readOnly = globalReadOnly
case !ok:
readOnly = v6.ReadOnlyMissing
case service.IsSystem:
Expand Down Expand Up @@ -148,9 +156,14 @@ func (d *Daemon) ListImages(ctx context.Context, spec update.ResourceSpec) ([]v6
return nil, errors.Wrap(err, "getting images for services")
}

policyResourceMap, _, err := d.getPolicyResourceMap(ctx)
if err != nil {
return nil, err
}

var res []v6.ImageStatus
for _, service := range services {
serviceContainers := getServiceContainers(service, imageRepos)
serviceContainers := getServiceContainers(service, imageRepos, policyResourceMap)
res = append(res, v6.ImageStatus{
ID: service.ID,
Containers: serviceContainers,
Expand Down Expand Up @@ -544,20 +557,52 @@ func containers2containers(cs []resource.Container) []v6.Container {
return res
}

func getServiceContainers(service cluster.Controller, imageRepos update.ImageRepos) (res []v6.Container) {
func getServiceContainers(service cluster.Controller, imageRepos update.ImageRepos, policyResourceMap policy.ResourceMap) (res []v6.Container) {
for _, c := range service.ContainersOrNil() {
available := imageRepos.Available(c.Image.Name)
availableErr := ""
if available == nil {
availableErr = registry.ErrNoImageData.Error()
imageRepo := c.Image.Name
tagPattern := getTagPattern(policyResourceMap, service.ID, c.Name)

currentImage := imageRepos.FindImageInfo(imageRepo, c.Image)
latestFilteredImage, _ := imageRepos.LatestFilteredImage(imageRepo, tagPattern)

// All available images
availableImages := imageRepos.Available(imageRepo)
availableImagesCount := len(availableImages)
availableImagesErr := ""
if availableImages == nil {
availableImagesErr = registry.ErrNoImageData.Error()
}
var newAvailableImages []image.Info
for _, img := range availableImages {
if img.CreatedAt.After(currentImage.CreatedAt) {
newAvailableImages = append(newAvailableImages, img)
}
}
newAvailableImagesCount := len(newAvailableImages)

// Filtered available images
filteredImages := imageRepos.FilteredAvailable(imageRepo, tagPattern)
filteredImagesCount := len(filteredImages)
var newFilteredImages []image.Info
for _, img := range filteredImages {
if img.CreatedAt.After(currentImage.CreatedAt) {
newFilteredImages = append(newFilteredImages, img)
}
}
newFilteredImagesCount := len(newFilteredImages)

res = append(res, v6.Container{
Name: c.Name,
Current: image.Info{
ID: c.Image,
},
Available: available,
AvailableError: availableErr,
Name: c.Name,
Current: currentImage,
LatestFiltered: latestFilteredImage,

Available: availableImages,
AvailableError: availableImagesErr,
AvailableImagesCount: availableImagesCount,
NewAvailableImagesCount: newAvailableImagesCount,

FilteredImagesCount: filteredImagesCount,
NewFilteredImagesCount: newFilteredImagesCount,
})
}
return res
Expand Down
16 changes: 10 additions & 6 deletions daemon/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ func (d *Daemon) pollForNewImages(logger log.Logger) {

ctx := context.Background()

candidateServices, err := d.unlockedAutomatedServices(ctx)
candidateServicesPolicyMap, err := d.getUnlockedAutomatedServicesPolicyMap(ctx)

This comment was marked as abuse.

This comment was marked as abuse.

if err != nil {
logger.Log("error", errors.Wrap(err, "getting unlocked automated services"))
return
}
if len(candidateServices) == 0 {
if len(candidateServicesPolicyMap) == 0 {
logger.Log("msg", "no automated services")
return
}
// Find images to check
services, err := d.Cluster.SomeControllers(candidateServices.ToSlice())
services, err := d.Cluster.SomeControllers(candidateServicesPolicyMap.ToSlice())
if err != nil {
logger.Log("error", errors.Wrap(err, "checking services for new images"))
return
Expand All @@ -51,11 +51,11 @@ func (d *Daemon) pollForNewImages(logger log.Logger) {
continue
}

pattern := getTagPattern(candidateServices, service.ID, container.Name)
pattern := getTagPattern(candidateServicesPolicyMap, service.ID, container.Name)
repo := currentImageID.Name
logger.Log("repo", repo, "pattern", pattern)

if latest, ok := imageRepos.LatestImage(repo, pattern); ok && latest.ID != currentImageID {
if latest, ok := imageRepos.LatestFilteredImage(repo, pattern); ok && latest.ID != currentImageID {
if latest.ID.Tag == "" {
logger.Log("msg", "untagged image in available images", "action", "skip", "available", repo)
continue
Expand All @@ -73,14 +73,18 @@ func (d *Daemon) pollForNewImages(logger log.Logger) {
}

func getTagPattern(services policy.ResourceMap, service flux.ResourceID, container string) string {
if services == nil {
return "*"
}
policies := services[service]
if pattern, ok := policies.Get(policy.TagPrefix(container)); ok {
return strings.TrimPrefix(pattern, "glob:")
}
return "*"
}

func (d *Daemon) unlockedAutomatedServices(ctx context.Context) (policy.ResourceMap, error) {
// getUnlockedAutomatedServicesPolicyMap returns a resource policy map for all unlocked automated services
func (d *Daemon) getUnlockedAutomatedServicesPolicyMap(ctx context.Context) (policy.ResourceMap, error) {
var services policy.ResourceMap
err := d.WithClone(ctx, func(checkout *git.Checkout) error {
var err error
Expand Down
36 changes: 31 additions & 5 deletions update/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,40 @@ type ImageRepos struct {
imageRepos imageReposMap
}

// LatestImage returns the latest releasable image for a repository
// FindImageInfo retruns image.Info given an image ref. If the image cannot be

This comment was marked as abuse.

// found, return the image.Info with only rhe ID.

This comment was marked as abuse.

func (r ImageRepos) FindImageInfo(repo image.Name, ref image.Ref) image.Info {
images, ok := r.imageRepos[ref.CanonicalName()]
if !ok {
return image.Info{ID: ref}
}
for _, img := range images {
if img.ID == ref {
return img
}
}
return image.Info{ID: ref}
}

// LatestFilteredImage returns the latest releasable image for a repository
// for which the tag matches a given pattern. A releasable image is
// one that is not tagged "latest". (Assumes the available images are
// in descending order of latestness.) If no such image exists,
// returns a zero value and `false`, and the caller can decide whether
// that's an error or not.
func (r ImageRepos) LatestImage(repo image.Name, tagGlob string) (image.Info, bool) {
for _, available := range r.imageRepos[repo.CanonicalName()] {
func (r ImageRepos) LatestFilteredImage(repo image.Name, tagGlob string) (image.Info, bool) {

This comment was marked as abuse.

This comment was marked as abuse.

filtered := r.FilteredAvailable(repo, tagGlob)
if len(filtered) > 0 {
return filtered[0], true
}
return image.Info{}, false
}

// FilteredAvailable returns image.Info engtries for all the images in the

This comment was marked as abuse.

// names image repository which match the tagGlob.

This comment was marked as abuse.

func (r ImageRepos) FilteredAvailable(repo image.Name, tagGlob string) []image.Info {

This comment was marked as abuse.

var filtered []image.Info
for _, available := range r.Available(repo) {
tag := available.ID.Tag
// Ignore latest if and only if it's not what the user wants.
if !strings.EqualFold(tagGlob, "latest") && strings.EqualFold(tag, "latest") {
Expand All @@ -38,10 +64,10 @@ func (r ImageRepos) LatestImage(repo image.Name, tagGlob string) (image.Info, bo
var im image.Info
im = available
im.ID = repo.ToRef(tag)
return im, true
filtered = append(filtered, im)
}
}
return image.Info{}, false
return filtered
}

// Available returns image.Info entries for all the images in the
Expand Down
2 changes: 1 addition & 1 deletion update/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func (s ReleaseSpec) calculateImageUpdates(rc ReleaseContext, candidates []*Cont
for _, container := range containers {
currentImageID := container.Image

latestImage, ok := imageRepos.LatestImage(currentImageID.Name, "*")
latestImage, ok := imageRepos.LatestFilteredImage(currentImageID.Name, "*")
if !ok {
if currentImageID.CanonicalName() != singleRepo {
ignoredOrSkipped = ReleaseStatusIgnored
Expand Down