diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a0e5356..6e5f2ab5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.4.1 (2019/06/08) + +* Filter tags before return them + ## 0.4.0 (2019/06/08) * Add option to set the maximum number of tags to watch for an item if `watch_repo` is enabled diff --git a/README.md b/README.md index 971d027c..38ffea36 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Choose the archive matching the destination platform and extract diun: ``` $ cd /opt -$ wget -qO- https://github.com/crazy-max/diun/releases/download/v0.4.0/diun_0.4.0_linux_x86_64.tar.gz | tar -zxvf - diun +$ wget -qO- https://github.com/crazy-max/diun/releases/download/v0.4.1/diun_0.4.1_linux_x86_64.tar.gz | tar -zxvf - diun ``` After getting the binary, it can be tested with `./diun --help` or moved to a permanent location. @@ -141,7 +141,7 @@ items: watch_repo: true max_tags: 10 include_tags: - - ^(0|[1-9]\d*)\.* + - ^(0|[1-9]\d*)\..* ``` * `db` diff --git a/internal/app/diun.go b/internal/app/diun.go index 929f556f..3ed4a4c1 100644 --- a/internal/app/diun.go +++ b/internal/app/diun.go @@ -87,10 +87,10 @@ func (di *Diun) analyzeImage(imageStr string, item model.Item, reg *docker.Regis return registry.Image{}, fmt.Errorf("cannot parse image name %s: %v", item.Image, err) } - if !di.isIncluded(image.Tag, item.IncludeTags) { + if !utl.IsIncluded(image.Tag, item.IncludeTags) { log.Warn().Str("image", image.String()).Msgf("Tag %s not included", image.Tag) return image, nil - } else if di.isExcluded(image.Tag, item.ExcludeTags) { + } else if utl.IsExcluded(image.Tag, item.ExcludeTags) { log.Warn().Str("image", image.String()).Msgf("Tag %s excluded", image.Tag) return image, nil } @@ -135,14 +135,25 @@ func (di *Diun) analyzeImage(imageStr string, item model.Item, reg *docker.Regis } func (di *Diun) analyzeRepo(image registry.Image, item model.Item, reg *docker.RegistryClient) { - tags, tagsCount, err := reg.Tags(image, item.MaxTags) + tags, err := reg.Tags(docker.TagsOptions{ + Image: image, + Max: item.MaxTags, + Include: item.IncludeTags, + Exclude: item.ExcludeTags, + }) if err != nil { log.Error().Err(err).Str("image", image.String()).Msg("Cannot retrieve tags") return } - log.Debug().Str("image", image.String()).Msgf("%d tag(s) found in repository. %d will be analyzed.", tagsCount, len(tags)) + log.Debug().Str("image", image.String()).Msgf("%d tag(s) found in repository. %d will be analyzed (%d max, %d not included, %d excluded).", + tags.Total, + len(tags.List), + item.MaxTags, + tags.NotIncluded, + tags.Excluded, + ) - for _, tag := range tags { + for _, tag := range tags.List { imageStr := fmt.Sprintf("%s/%s:%s", image.Domain, image.Path, tag) if _, err := di.analyzeImage(imageStr, item, reg); err != nil { log.Error().Err(err).Str("image", imageStr).Msg("Cannot analyze image") @@ -158,30 +169,6 @@ func (di *Diun) Close() { } } -func (di *Diun) isIncluded(tag string, includes []string) bool { - if len(includes) == 0 { - return true - } - for _, include := range includes { - if utl.MatchString(include, tag) { - return true - } - } - return false -} - -func (di *Diun) isExcluded(tag string, excludes []string) bool { - if len(excludes) == 0 { - return false - } - for _, exclude := range excludes { - if utl.MatchString(exclude, tag) { - return true - } - } - return false -} - func (di *Diun) trackTime(start time.Time, prefix string) { log.Info().Msgf("%s%s", prefix, durafmt.ParseShort(time.Since(start)).String()) } diff --git a/internal/utl/utl.go b/internal/utl/utl.go index 110c84af..f8f64ac7 100644 --- a/internal/utl/utl.go +++ b/internal/utl/utl.go @@ -13,3 +13,31 @@ func MatchString(exp string, s string) bool { } return re.MatchString(s) } + +// IsIncluded checks if s string is included in includes +// If includes is empty, assume true +func IsIncluded(s string, includes []string) bool { + if len(includes) == 0 { + return true + } + for _, include := range includes { + if MatchString(include, s) { + return true + } + } + return false +} + +// IsExcluded checks if s string is excluded in excludes +// If excludes is empty, assume false +func IsExcluded(s string, excludes []string) bool { + if len(excludes) == 0 { + return false + } + for _, exclude := range excludes { + if MatchString(exclude, s) { + return true + } + } + return false +} diff --git a/pkg/docker/tags.go b/pkg/docker/tags.go index c6206f18..3f19f1d5 100644 --- a/pkg/docker/tags.go +++ b/pkg/docker/tags.go @@ -2,36 +2,67 @@ package docker import ( "github.com/containers/image/docker" + "github.com/crazy-max/diun/internal/utl" "github.com/crazy-max/diun/pkg/docker/registry" ) -type Tags []string +type Tags struct { + List []string + NotIncluded int + Excluded int + Total int +} + +type TagsOptions struct { + Image registry.Image + Max int + Include []string + Exclude []string +} // Tags returns tags of a Docker repository -func (c *RegistryClient) Tags(image registry.Image, max int) (Tags, int, error) { +func (c *RegistryClient) Tags(opts TagsOptions) (*Tags, error) { ctx, cancel := c.timeoutContext() defer cancel() - imgCls, err := c.newImage(ctx, image.String()) + imgCls, err := c.newImage(ctx, opts.Image.String()) if err != nil { - return nil, 0, err + return nil, err } defer imgCls.Close() tags, err := docker.GetRepositoryTags(ctx, c.sysCtx, imgCls.Reference()) if err != nil { - return nil, 0, err + 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(tags)/2 - 1; i >= 0; i-- { - opp := len(tags) - 1 - i - tags[i], tags[opp] = tags[opp], tags[i] + 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 max > 0 && len(tags) >= max { - return Tags(tags[:max]), len(tags), nil + if opts.Max > 0 && len(res.List) >= opts.Max { + res.List = res.List[:opts.Max] } - return Tags(tags), len(tags), nil + return res, nil }