chore: use typed error and fix missing wraps

This commit is contained in:
CrazyMax
2023-09-16 11:01:31 +02:00
parent 20681d68d1
commit 24bc0548e0
24 changed files with 109 additions and 94 deletions

View File

@@ -30,6 +30,9 @@ linters-settings:
# The io/ioutil package has been deprecated. # The io/ioutil package has been deprecated.
# https://go.dev/doc/go1.16#ioutil # https://go.dev/doc/go1.16#ioutil
- io/ioutil - io/ioutil
forbidigo:
forbid:
- '^fmt\.Errorf(# use errors\.Errorf instead)?$'
importas: importas:
no-unaliased: true no-unaliased: true

View File

@@ -75,7 +75,7 @@ func New(meta model.Meta, cfg *config.Config, grpcAuthority string) (*Diun, erro
if len(cfg.Watch.Healthchecks.BaseURL) > 0 { if len(cfg.Watch.Healthchecks.BaseURL) > 0 {
hcBaseURL, err = url.Parse(cfg.Watch.Healthchecks.BaseURL) hcBaseURL, err = url.Parse(cfg.Watch.Healthchecks.BaseURL)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "Cannot parse Healthchecks base URL") return nil, errors.Wrap(err, "cannot parse Healthchecks base URL")
} }
} }
diun.hc = gohealthchecks.NewClient(&gohealthchecks.ClientOptions{ diun.hc = gohealthchecks.NewClient(&gohealthchecks.ClientOptions{

View File

@@ -36,7 +36,7 @@ func Load(config string) (*Config, error) {
}, },
}) })
if found, err := fileLoader.Load(&cfg); err != nil { if found, err := fileLoader.Load(&cfg); err != nil {
return nil, errors.Wrap(err, "Failed to decode configuration from file") return nil, errors.Wrap(err, "failed to decode configuration from file")
} else if !found { } else if !found {
log.Debug().Msg("No configuration file found") log.Debug().Msg("No configuration file found")
} else { } else {
@@ -47,7 +47,7 @@ func Load(config string) (*Config, error) {
Prefix: "DIUN_", Prefix: "DIUN_",
}) })
if found, err := envLoader.Load(&cfg); err != nil { if found, err := envLoader.Load(&cfg); err != nil {
return nil, errors.Wrap(err, "Failed to decode configuration from environment variables") return nil, errors.Wrap(err, "failed to decode configuration from environment variables")
} else if !found { } else if !found {
log.Debug().Msg("No DIUN_* environment variables defined") log.Debug().Msg("No DIUN_* environment variables defined")
} else { } else {
@@ -64,16 +64,16 @@ func Load(config string) (*Config, error) {
func (cfg *Config) validate() error { func (cfg *Config) validate() error {
if len(cfg.Db.Path) > 0 { if len(cfg.Db.Path) > 0 {
if err := os.MkdirAll(path.Dir(cfg.Db.Path), os.ModePerm); err != nil { if err := os.MkdirAll(path.Dir(cfg.Db.Path), os.ModePerm); err != nil {
return errors.Wrap(err, "Cannot create database destination folder") return errors.Wrap(err, "cannot create database destination folder")
} }
} }
if cfg.Watch.Healthchecks != nil && len(cfg.Watch.Healthchecks.UUID) == 0 { if cfg.Watch.Healthchecks != nil && len(cfg.Watch.Healthchecks.UUID) == 0 {
return errors.New("Healthchecks UUID is required") return errors.New("healthchecks UUID is required")
} }
if cfg.Providers == nil { if cfg.Providers == nil {
return errors.New("At least one provider is required") return errors.New("at least one provider is required")
} }
return validator.New().Struct(cfg) return validator.New().Struct(cfg)

View File

@@ -52,7 +52,7 @@ func New(cfg model.Db) (*Client, error) {
log.Debug().Msgf("%d entries found in manifest bucket", stats.KeyN) log.Debug().Msgf("%d entries found in manifest bucket", stats.KeyN)
return nil return nil
}); err != nil { }); err != nil {
return nil, errors.Wrap(err, "Cannot count entries in manifest bucket") return nil, errors.Wrap(err, "cannot count entries in manifest bucket")
} }
c := &Client{ c := &Client{

View File

@@ -2,7 +2,6 @@ package db
import ( import (
"encoding/json" "encoding/json"
"fmt"
"time" "time"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
@@ -24,12 +23,12 @@ func (c *Client) Migrate() error {
for version := c.metadata.Version + 1; version <= dbVersion; version++ { for version := c.metadata.Version + 1; version <= dbVersion; version++ {
migration, found := migrations[version] migration, found := migrations[version]
if !found { if !found {
return fmt.Errorf("database migration v%d not found", version) return errors.Errorf("database migration v%d not found", version)
} }
log.Info().Msgf("Database migration v%d...", version) log.Info().Msgf("Database migration v%d...", version)
if err := migration(c); err != nil { if err := migration(c); err != nil {
return errors.Wrapf(err, "Database migration v%d failed", version) return errors.Wrapf(err, "database migration v%d failed", version)
} }
} }

View File

@@ -46,7 +46,7 @@ func (c *Client) Start() error {
lis, err := net.Listen("tcp", c.authority) lis, err := net.Listen("tcp", c.authority)
if err != nil { if err != nil {
return errors.Wrap(err, "Cannot create gRPC listener") return errors.Wrap(err, "cannot create gRPC listener")
} }
return c.server.Serve(lis) return c.server.Serve(lis)

View File

@@ -7,6 +7,7 @@ import (
"github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/docker/reference"
"github.com/crazy-max/diun/v4/pb" "github.com/crazy-max/diun/v4/pb"
"github.com/pkg/errors"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
) )
@@ -55,7 +56,7 @@ func (c *Client) ImageInspect(ctx context.Context, request *pb.ImageInspectReque
} }
if _, ok := images[ref.Name()]; !ok { if _, ok := images[ref.Name()]; !ok {
return nil, fmt.Errorf("%s not found in database", ref.Name()) return nil, errors.Errorf("%s not found in database", ref.Name())
} }
iir := &pb.ImageInspectResponse_Image{ iir := &pb.ImageInspectResponse_Image{

View File

@@ -1,12 +1,12 @@
package model package model
import ( import (
"fmt"
"strings" "strings"
"time" "time"
"github.com/crazy-max/diun/v4/pkg/registry" "github.com/crazy-max/diun/v4/pkg/registry"
"github.com/crazy-max/diun/v4/pkg/utl" "github.com/crazy-max/diun/v4/pkg/utl"
"github.com/pkg/errors"
) )
// RegOpts holds slice of registry options // RegOpts holds slice of registry options
@@ -60,5 +60,5 @@ func (s *RegOpts) Select(name string, image registry.Image) (*RegOpt, error) {
if len(name) == 0 { if len(name) == 0 {
return nil, nil return nil, nil
} }
return nil, fmt.Errorf("%s not found", name) return nil, errors.Errorf("%s not found", name)
} }

View File

@@ -41,7 +41,7 @@ func (c *Client) RenderMarkdown() (title []byte, body []byte, _ error) {
var titleBuf bytes.Buffer var titleBuf bytes.Buffer
titleTpl, err := template.New("title").Funcs(c.opts.TemplateFuncs).Parse(strings.TrimSuffix(strings.TrimSpace(c.opts.TemplateTitle), "\n")) titleTpl, err := template.New("title").Funcs(c.opts.TemplateFuncs).Parse(strings.TrimSuffix(strings.TrimSpace(c.opts.TemplateTitle), "\n"))
if err != nil { if err != nil {
return title, body, errors.Wrap(err, "Cannot parse title template") return title, body, errors.Wrap(err, "cannot parse title template")
} }
if err = titleTpl.Execute(&titleBuf, struct { if err = titleTpl.Execute(&titleBuf, struct {
Meta model.Meta Meta model.Meta
@@ -50,14 +50,14 @@ func (c *Client) RenderMarkdown() (title []byte, body []byte, _ error) {
Meta: c.opts.Meta, Meta: c.opts.Meta,
Entry: c.opts.Entry, Entry: c.opts.Entry,
}); err != nil { }); err != nil {
return title, body, errors.Wrap(err, "Cannot render notif title") return title, body, errors.Wrap(err, "cannot render notif title")
} }
title = titleBuf.Bytes() title = titleBuf.Bytes()
var bodyBuf bytes.Buffer var bodyBuf bytes.Buffer
bodyTpl, err := template.New("body").Funcs(c.opts.TemplateFuncs).Parse(strings.TrimSuffix(strings.TrimSpace(c.opts.TemplateBody), "\n")) bodyTpl, err := template.New("body").Funcs(c.opts.TemplateFuncs).Parse(strings.TrimSuffix(strings.TrimSpace(c.opts.TemplateBody), "\n"))
if err != nil { if err != nil {
return title, body, errors.Wrap(err, "Cannot parse body template") return title, body, errors.Wrap(err, "cannot parse body template")
} }
if err = bodyTpl.Execute(&bodyBuf, struct { if err = bodyTpl.Execute(&bodyBuf, struct {
Meta model.Meta Meta model.Meta
@@ -66,7 +66,7 @@ func (c *Client) RenderMarkdown() (title []byte, body []byte, _ error) {
Meta: c.opts.Meta, Meta: c.opts.Meta,
Entry: c.opts.Entry, Entry: c.opts.Entry,
}); err != nil { }); err != nil {
return title, body, errors.Wrap(err, "Cannot render notif body") return title, body, errors.Wrap(err, "cannot render notif body")
} }
body = bodyBuf.Bytes() body = bodyBuf.Bytes()

View File

@@ -11,6 +11,7 @@ import (
"github.com/crazy-max/diun/v4/internal/model" "github.com/crazy-max/diun/v4/internal/model"
"github.com/crazy-max/diun/v4/internal/msg" "github.com/crazy-max/diun/v4/internal/msg"
"github.com/crazy-max/diun/v4/internal/notif/notifier" "github.com/crazy-max/diun/v4/internal/notif/notifier"
"github.com/pkg/errors"
) )
// Client represents an active discord notification object // Client represents an active discord notification object
@@ -138,7 +139,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
} }
if resp.StatusCode != http.StatusNoContent { if resp.StatusCode != http.StatusNoContent {
return fmt.Errorf("unexpected HTTP status %d: %s", resp.StatusCode, resp.Body) return errors.Errorf("unexpected HTTP status %d: %s", resp.StatusCode, resp.Body)
} }
return nil return nil

View File

@@ -3,7 +3,6 @@ package gotify
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
@@ -42,7 +41,7 @@ func (c *Client) Name() string {
func (c *Client) Send(entry model.NotifEntry) error { func (c *Client) Send(entry model.NotifEntry) error {
token, err := utl.GetSecret(c.cfg.Token, c.cfg.TokenFile) token, err := utl.GetSecret(c.cfg.Token, c.cfg.TokenFile)
if err != nil { if err != nil {
return errors.New("Cannot retrieve token secret for Gotify notifier") return errors.Wrap(err, "cannot retrieve token secret for Gotify notifier")
} }
hc := http.Client{ hc := http.Client{
@@ -117,7 +116,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
if err != nil { if err != nil {
return err return err
} }
return fmt.Errorf("%d %s: %s", errBody.ErrorCode, errBody.Error, errBody.ErrorDescription) return errors.Errorf("%d %s: %s", errBody.ErrorCode, errBody.Error, errBody.ErrorDescription)
} }
return nil return nil

View File

@@ -13,6 +13,7 @@ import (
"github.com/crazy-max/diun/v4/pkg/utl" "github.com/crazy-max/diun/v4/pkg/utl"
"github.com/go-gomail/gomail" "github.com/go-gomail/gomail"
"github.com/matcornic/hermes/v2" "github.com/matcornic/hermes/v2"
"github.com/pkg/errors"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
@@ -89,13 +90,13 @@ func (c *Client) Send(entry model.NotifEntry) error {
// Generate an HTML email with the provided contents (for modern clients) // Generate an HTML email with the provided contents (for modern clients)
htmlpart, err := h.GenerateHTML(email) htmlpart, err := h.GenerateHTML(email)
if err != nil { if err != nil {
return fmt.Errorf("hermes: %v", err) return errors.Wrap(err, "cannot generate HTML email")
} }
// Generate the plaintext version of the e-mail (for clients that do not support xHTML) // Generate the plaintext version of the e-mail (for clients that do not support xHTML)
textpart, err := h.GeneratePlainText(email) textpart, err := h.GeneratePlainText(email)
if err != nil { if err != nil {
return fmt.Errorf("hermes: %v", err) return errors.Wrap(err, "cannot generate plaintext email")
} }
mailMessage := gomail.NewMessage() mailMessage := gomail.NewMessage()

View File

@@ -48,11 +48,11 @@ func (c *Client) Send(entry model.NotifEntry) error {
user, err := utl.GetSecret(c.cfg.User, c.cfg.UserFile) user, err := utl.GetSecret(c.cfg.User, c.cfg.UserFile)
if err != nil { if err != nil {
return errors.New("Cannot retrieve username secret for Matrix notifier") return errors.Wrap(err, "cannot retrieve username secret for Matrix notifier")
} }
password, err := utl.GetSecret(c.cfg.Password, c.cfg.PasswordFile) password, err := utl.GetSecret(c.cfg.Password, c.cfg.PasswordFile)
if err != nil { if err != nil {
return errors.New("Cannot retrieve password secret for Matrix notifier") return errors.Wrap(err, "cannot retrieve password secret for Matrix notifier")
} }
r, err := m.Login(&gomatrix.ReqLogin{ r, err := m.Login(&gomatrix.ReqLogin{

View File

@@ -11,6 +11,7 @@ import (
"github.com/crazy-max/diun/v4/internal/msg" "github.com/crazy-max/diun/v4/internal/msg"
"github.com/crazy-max/diun/v4/internal/notif/notifier" "github.com/crazy-max/diun/v4/internal/notif/notifier"
"github.com/crazy-max/diun/v4/pkg/utl" "github.com/crazy-max/diun/v4/pkg/utl"
"github.com/pkg/errors"
) )
// Client represents an active ntfy notification object // Client represents an active ntfy notification object
@@ -107,7 +108,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
if err != nil { if err != nil {
return err return err
} }
return fmt.Errorf("%d %s: %s", errBody.ErrorCode, errBody.Error, errBody.ErrorDescription) return errors.Errorf("%d %s: %s", errBody.ErrorCode, errBody.Error, errBody.ErrorDescription)
} }
return nil return nil

View File

@@ -37,12 +37,12 @@ func (c *Client) Name() string {
func (c *Client) Send(entry model.NotifEntry) error { func (c *Client) Send(entry model.NotifEntry) error {
token, err := utl.GetSecret(c.cfg.Token, c.cfg.TokenFile) token, err := utl.GetSecret(c.cfg.Token, c.cfg.TokenFile)
if err != nil { if err != nil {
return errors.New("Cannot retrieve token secret for Pushover notifier") return errors.Wrap(err, "cannot retrieve token secret for Pushover notifier")
} }
recipient, err := utl.GetSecret(c.cfg.Recipient, c.cfg.RecipientFile) recipient, err := utl.GetSecret(c.cfg.Recipient, c.cfg.RecipientFile)
if err != nil { if err != nil {
return errors.New("Cannot retrieve recipient secret for Pushover notifier") return errors.Wrap(err, "cannot retrieve recipient secret for Pushover notifier")
} }
message, err := msg.New(msg.Options{ message, err := msg.New(msg.Options{

View File

@@ -3,7 +3,6 @@ package rocketchat
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
@@ -44,7 +43,7 @@ func (c *Client) Name() string {
func (c *Client) Send(entry model.NotifEntry) error { func (c *Client) Send(entry model.NotifEntry) error {
token, err := utl.GetSecret(c.cfg.Token, c.cfg.TokenFile) token, err := utl.GetSecret(c.cfg.Token, c.cfg.TokenFile)
if err != nil { if err != nil {
return errors.New("Cannot retrieve token secret for RocketChat notifier") return errors.Wrap(err, "cannot retrieve token secret for RocketChat notifier")
} }
hc := http.Client{ hc := http.Client{
@@ -152,7 +151,7 @@ func (c *Client) Send(entry model.NotifEntry) error {
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return fmt.Errorf("HTTP error %d: %s", resp.StatusCode, respBody.ErrorType) return errors.Errorf("unexpected HTTP error %d: %s", resp.StatusCode, respBody.ErrorType)
} }
return nil return nil

View File

@@ -39,17 +39,17 @@ func (c *Client) Name() string {
func (c *Client) Send(entry model.NotifEntry) error { func (c *Client) Send(entry model.NotifEntry) error {
token, err := utl.GetSecret(c.cfg.Token, c.cfg.TokenFile) token, err := utl.GetSecret(c.cfg.Token, c.cfg.TokenFile)
if err != nil { if err != nil {
return errors.New("Cannot retrieve token secret for Telegram notifier") return errors.Wrap(err, "cannot retrieve token secret for Telegram notifier")
} }
chatIDs := c.cfg.ChatIDs chatIDs := c.cfg.ChatIDs
chatIDsRaw, err := utl.GetSecret("", c.cfg.ChatIDsFile) chatIDsRaw, err := utl.GetSecret("", c.cfg.ChatIDsFile)
if err != nil { if err != nil {
return errors.New("Cannot retrieve chat IDs secret for Telegram notifier") return errors.Wrap(err, "cannot retrieve chat IDs secret for Telegram notifier")
} }
if len(chatIDsRaw) > 0 { if len(chatIDsRaw) > 0 {
if err = json.Unmarshal([]byte(chatIDsRaw), &chatIDs); err != nil { if err = json.Unmarshal([]byte(chatIDsRaw), &chatIDs); err != nil {
return errors.New("Cannot unmarshal chat IDs secret for Telegram notifier") return errors.Wrap(err, "cannot unmarshal chat IDs secret for Telegram notifier")
} }
} }

View File

@@ -1,8 +1,6 @@
package provider package provider
import ( import (
"errors"
"fmt"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@@ -11,12 +9,12 @@ import (
"github.com/crazy-max/diun/v4/internal/model" "github.com/crazy-max/diun/v4/internal/model"
"github.com/crazy-max/diun/v4/pkg/registry" "github.com/crazy-max/diun/v4/pkg/registry"
"github.com/imdario/mergo" "github.com/imdario/mergo"
"github.com/pkg/errors"
) )
var ( var (
metadataKeyChars = `a-zA-Z0-9_` metadataKeyChars = `a-zA-Z0-9_`
metadataKeyRegexp = regexp.MustCompile(`^[` + metadataKeyChars + `]+$`) metadataKeyRegexp = regexp.MustCompile(`^[` + metadataKeyChars + `]+$`)
errInvalidLabel = errors.New("invalid label error")
) )
// ValidateImage returns a standard image through Docker labels // ValidateImage returns a standard image through Docker labels
@@ -30,13 +28,13 @@ func ValidateImage(image string, metadata, labels map[string]string, watchByDef
} }
if err := mergo.Merge(&img, imageDefaults); err != nil { if err := mergo.Merge(&img, imageDefaults); err != nil {
return img, fmt.Errorf("failed to merge image defaults for image %s", image) return img, &invalidLabelError{errors.Wrapf(err, "failed to merge image defaults for image %s", image)}
} }
if enableStr, ok := labels["diun.enable"]; ok { if enableStr, ok := labels["diun.enable"]; ok {
enable, err := strconv.ParseBool(enableStr) enable, err := strconv.ParseBool(enableStr)
if err != nil { if err != nil {
return img, fmt.Errorf("cannot parse %q value of label diun.enable: %w", enableStr, errInvalidLabel) return img, &invalidLabelError{errors.Wrapf(err, "cannot parse %q value of label diun.enable", enableStr)}
} }
if !enable { if !enable {
return model.Image{}, nil return model.Image{}, nil
@@ -53,7 +51,7 @@ func ValidateImage(image string, metadata, labels map[string]string, watchByDef
if watchRepo, err := strconv.ParseBool(value); err == nil { if watchRepo, err := strconv.ParseBool(value); err == nil {
img.WatchRepo = &watchRepo img.WatchRepo = &watchRepo
} else { } else {
return img, fmt.Errorf("cannot parse %q value of label %s: %w", value, key, errInvalidLabel) return img, &invalidLabelError{errors.Wrapf(err, "cannot parse %q value of label %s", value, key)}
} }
case key == "diun.notify_on": case key == "diun.notify_on":
if len(value) == 0 { if len(value) == 0 {
@@ -63,7 +61,7 @@ func ValidateImage(image string, metadata, labels map[string]string, watchByDef
for _, no := range strings.Split(value, ";") { for _, no := range strings.Split(value, ";") {
notifyOn := model.NotifyOn(no) notifyOn := model.NotifyOn(no)
if !notifyOn.Valid() { if !notifyOn.Valid() {
return img, fmt.Errorf("unknown notify status %q: %w", value, errInvalidLabel) return img, &invalidLabelError{errors.Errorf("unknown notify status %q", value)}
} }
img.NotifyOn = append(img.NotifyOn, notifyOn) img.NotifyOn = append(img.NotifyOn, notifyOn)
} }
@@ -73,12 +71,12 @@ func ValidateImage(image string, metadata, labels map[string]string, watchByDef
} }
sortTags := registry.SortTag(value) sortTags := registry.SortTag(value)
if !sortTags.Valid() { if !sortTags.Valid() {
return img, fmt.Errorf("unknown sort tags type %q: %w", value, errInvalidLabel) return img, &invalidLabelError{errors.Errorf("unknown sort tags type %q", value)}
} }
img.SortTags = sortTags img.SortTags = sortTags
case key == "diun.max_tags": case key == "diun.max_tags":
if img.MaxTags, err = strconv.Atoi(value); err != nil { if img.MaxTags, err = strconv.Atoi(value); err != nil {
return img, fmt.Errorf("cannot parse %q value of label %s: %w", value, key, errInvalidLabel) return img, &invalidLabelError{errors.Wrapf(err, "cannot parse %q value of label %s", value, key)}
} }
case key == "diun.include_tags": case key == "diun.include_tags":
img.IncludeTags = strings.Split(value, ";") img.IncludeTags = strings.Split(value, ";")
@@ -91,7 +89,7 @@ func ValidateImage(image string, metadata, labels map[string]string, watchByDef
case key == "diun.platform": case key == "diun.platform":
platform, err := platforms.Parse(value) platform, err := platforms.Parse(value)
if err != nil { if err != nil {
return img, fmt.Errorf("cannot parse %q platform of label %s: %w", value, key, errInvalidLabel) return img, &invalidLabelError{errors.Wrapf(err, "cannot parse %q platform of label %s", value, key)}
} }
img.Platform = model.ImagePlatform{ img.Platform = model.ImagePlatform{
OS: platform.OS, OS: platform.OS,
@@ -104,7 +102,7 @@ func ValidateImage(image string, metadata, labels map[string]string, watchByDef
break break
} }
if err := validateMetadataKey(mkey); err != nil { if err := validateMetadataKey(mkey); err != nil {
return img, fmt.Errorf("invalid metadata key %q: %w: %w", mkey, err, errInvalidLabel) return img, &invalidLabelError{errors.Wrapf(err, "invalid metadata key %q", mkey)}
} }
if img.Metadata == nil { if img.Metadata == nil {
img.Metadata = map[string]string{} img.Metadata = map[string]string{}
@@ -115,7 +113,7 @@ func ValidateImage(image string, metadata, labels map[string]string, watchByDef
// Update provider metadata with metadata from img labels // Update provider metadata with metadata from img labels
if err := mergo.Merge(&img.Metadata, metadata); err != nil { if err := mergo.Merge(&img.Metadata, metadata); err != nil {
return img, fmt.Errorf("failed merging metadata: %w", err) return img, errors.Wrapf(err, "failed merging metadata")
} }
return img, nil return img, nil
@@ -123,7 +121,19 @@ func ValidateImage(image string, metadata, labels map[string]string, watchByDef
func validateMetadataKey(key string) error { func validateMetadataKey(key string) error {
if !metadataKeyRegexp.MatchString(key) { if !metadataKeyRegexp.MatchString(key) {
return fmt.Errorf("only %q are allowed", metadataKeyChars) return errors.Errorf("only %q are allowed", metadataKeyChars)
} }
return nil return nil
} }
type invalidLabelError struct {
error
}
func (e *invalidLabelError) Error() string {
return e.Error()
}
func (e *invalidLabelError) Unwrap() error {
return e
}

View File

@@ -18,7 +18,7 @@ func TestValidateImage(t *testing.T) {
watchByDef bool watchByDef bool
imageDefaults model.Image imageDefaults model.Image
expectedImage model.Image expectedImage model.Image
expectedErr error expectedErr interface{}
}{ }{
// Test strip sha // Test strip sha
{ {
@@ -69,7 +69,7 @@ func TestValidateImage(t *testing.T) {
expectedErr: nil, expectedErr: nil,
}, },
{ {
name: "Invlaid diun.enable", name: "Invalid diun.enable",
image: "myimg", image: "myimg",
watchByDef: false, watchByDef: false,
labels: map[string]string{ labels: map[string]string{
@@ -78,7 +78,7 @@ func TestValidateImage(t *testing.T) {
expectedImage: model.Image{ expectedImage: model.Image{
Name: "myimg", Name: "myimg",
}, },
expectedErr: errInvalidLabel, expectedErr: &invalidLabelError{},
}, },
// Test diun.regopt // Test diun.regopt
{ {
@@ -164,7 +164,7 @@ func TestValidateImage(t *testing.T) {
expectedImage: model.Image{ expectedImage: model.Image{
Name: "myimg", Name: "myimg",
}, },
expectedErr: errInvalidLabel, expectedErr: &invalidLabelError{},
}, },
{ {
name: "Override default image values with labels (true > false)", name: "Override default image values with labels (true > false)",
@@ -225,7 +225,7 @@ func TestValidateImage(t *testing.T) {
Name: "myimg", Name: "myimg",
NotifyOn: []model.NotifyOn{}, NotifyOn: []model.NotifyOn{},
}, },
expectedErr: errInvalidLabel, expectedErr: &invalidLabelError{},
}, },
{ {
name: "Set empty notify_on", name: "Set empty notify_on",
@@ -296,7 +296,7 @@ func TestValidateImage(t *testing.T) {
expectedImage: model.Image{ expectedImage: model.Image{
Name: "myimg", Name: "myimg",
}, },
expectedErr: errInvalidLabel, expectedErr: &invalidLabelError{},
}, },
{ {
name: "Set empty sort_tags", name: "Set empty sort_tags",
@@ -367,7 +367,7 @@ func TestValidateImage(t *testing.T) {
expectedImage: model.Image{ expectedImage: model.Image{
Name: "myimg", Name: "myimg",
}, },
expectedErr: errInvalidLabel, expectedErr: &invalidLabelError{},
}, },
{ {
name: "Set empty max_tags", name: "Set empty max_tags",
@@ -380,7 +380,7 @@ func TestValidateImage(t *testing.T) {
expectedImage: model.Image{ expectedImage: model.Image{
Name: "myimg", Name: "myimg",
}, },
expectedErr: errInvalidLabel, expectedErr: &invalidLabelError{},
}, },
{ {
name: "Default max_tags", name: "Default max_tags",
@@ -678,7 +678,7 @@ func TestValidateImage(t *testing.T) {
expectedImage: model.Image{ expectedImage: model.Image{
Name: "myimg", Name: "myimg",
}, },
expectedErr: errInvalidLabel, expectedErr: &invalidLabelError{},
}, },
{ {
name: "Set empty platform", name: "Set empty platform",
@@ -692,7 +692,7 @@ func TestValidateImage(t *testing.T) {
Name: "myimg", Name: "myimg",
Platform: model.ImagePlatform{}, Platform: model.ImagePlatform{},
}, },
expectedErr: errInvalidLabel, expectedErr: &invalidLabelError{},
}, },
{ {
name: "Default platform", name: "Default platform",
@@ -768,7 +768,7 @@ func TestValidateImage(t *testing.T) {
expectedImage: model.Image{ expectedImage: model.Image{
Name: "myimg", Name: "myimg",
}, },
expectedErr: errInvalidLabel, expectedErr: &invalidLabelError{},
}, },
{ {
name: "Set empty metadata key", name: "Set empty metadata key",
@@ -855,25 +855,26 @@ func TestValidateImage(t *testing.T) {
}, },
} }
for _, c := range cases { for _, tt := range cases {
c := c tt := tt
t.Run(c.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Parallel() t.Parallel()
actualImg, actualErr := ValidateImage( img, err := ValidateImage(
c.image, tt.image,
c.metadata, tt.metadata,
c.labels, tt.labels,
c.watchByDef, tt.watchByDef,
c.imageDefaults, tt.imageDefaults,
) )
if tt.expectedErr == nil {
assert.Equal(t, c.expectedImage, actualImg) assert.NoError(t, err)
assert.Equal(t, tt.expectedImage, img)
if c.expectedErr == nil {
assert.NoError(t, actualErr)
} else { } else {
if assert.Error(t, c.expectedErr) { switch err.(type) {
assert.ErrorIs(t, actualErr, c.expectedErr) case *invalidLabelError:
assert.Error(t, err)
default:
assert.Error(t, err)
} }
} }
}) })

View File

@@ -27,17 +27,17 @@ type Options struct {
func New(opts Options) (*Client, error) { func New(opts Options) (*Client, error) {
b, err := os.ReadFile(opts.Filename) b, err := os.ReadFile(opts.Filename)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "Cannot read Dockerfile %s", opts.Filename) return nil, errors.Wrapf(err, "cannot read Dockerfile %s", opts.Filename)
} }
parsed, err := parser.Parse(bytes.NewReader(b)) parsed, err := parser.Parse(bytes.NewReader(b))
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "Cannot parse Dockerfile %s", opts.Filename) return nil, errors.Wrapf(err, "cannot parse Dockerfile %s", opts.Filename)
} }
stages, metaArgs, err := instructions.Parse(parsed.AST) stages, metaArgs, err := instructions.Parse(parsed.AST)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "Cannot parse stages for Dockerfile %s", opts.Filename) return nil, errors.Wrapf(err, "cannot parse stages for Dockerfile %s", opts.Filename)
} }
var kvpoArgs []instructions.KeyValuePairOptional var kvpoArgs []instructions.KeyValuePairOptional

View File

@@ -35,7 +35,7 @@ func (c *Client) FromImages() (Images, error) {
case command.From: case command.From:
ins, err := instructions.ParseInstruction(node) ins, err := instructions.ParseInstruction(node)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "Cannot parse instruction") return nil, errors.Wrapf(err, "cannot parse instruction")
} }
if baseName := ins.(*instructions.Stage).BaseName; baseName != "scratch" { if baseName := ins.(*instructions.Stage).BaseName; baseName != "scratch" {
name, err := c.shlex.ProcessWordWithMap(baseName, metaArgsToMap(c.metaArgs)) name, err := c.shlex.ProcessWordWithMap(baseName, metaArgsToMap(c.metaArgs))
@@ -56,7 +56,7 @@ func (c *Client) FromImages() (Images, error) {
case command.Copy: case command.Copy:
cmd, err := instructions.ParseCommand(node) cmd, err := instructions.ParseCommand(node)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "Cannot parse command") return nil, errors.Wrapf(err, "cannot parse command")
} }
if copyFrom := cmd.(*instructions.CopyCommand).From; copyFrom != "null" { if copyFrom := cmd.(*instructions.CopyCommand).From; copyFrom != "null" {
name, err := c.shlex.ProcessWordWithMap(copyFrom, metaArgsToMap(c.metaArgs)) name, err := c.shlex.ProcessWordWithMap(copyFrom, metaArgsToMap(c.metaArgs))
@@ -77,7 +77,7 @@ func (c *Client) FromImages() (Images, error) {
case command.Run: case command.Run:
cmd, err := instructions.ParseCommand(node) cmd, err := instructions.ParseCommand(node)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "Cannot parse command") return nil, errors.Wrapf(err, "cannot parse command")
} }
if cmdRun, ok := cmd.(*instructions.RunCommand); ok { if cmdRun, ok := cmd.(*instructions.RunCommand); ok {
mounts := instructions.GetMounts(cmdRun) mounts := instructions.GetMounts(cmdRun)

View File

@@ -90,12 +90,12 @@ func newExternalClusterClient(opts Options) (*kubernetes.Clientset, error) {
var err error var err error
if opts.Endpoint == "" { if opts.Endpoint == "" {
return nil, errors.New("Endpoint missing for external cluster client") return nil, errors.New("endpoint missing for external cluster client")
} }
opts.Token, err = utl.GetSecret(opts.Token, opts.TokenFile) opts.Token, err = utl.GetSecret(opts.Token, opts.TokenFile)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "Cannot retrieve bearer token") return nil, errors.Wrap(err, "cannot retrieve bearer token")
} }
config := &rest.Config{ config := &rest.Config{
@@ -106,7 +106,7 @@ func newExternalClusterClient(opts Options) (*kubernetes.Clientset, error) {
if opts.CertAuthFilePath != "" { if opts.CertAuthFilePath != "" {
caData, err := os.ReadFile(opts.CertAuthFilePath) caData, err := os.ReadFile(opts.CertAuthFilePath)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "Failed to read CA file") return nil, errors.Wrap(err, "failed to read CA file")
} }
config.TLSClientConfig = rest.TLSClientConfig{ config.TLSClientConfig = rest.TLSClientConfig{
CAData: caData, CAData: caData,

View File

@@ -32,13 +32,13 @@ func (c *Client) Manifest(image Image, dbManifest Manifest) (Manifest, bool, err
rmRef, err := ParseReference(image.String()) rmRef, err := ParseReference(image.String())
if err != nil { if err != nil {
return Manifest{}, false, errors.Wrap(err, "Cannot parse reference") return Manifest{}, false, errors.Wrap(err, "cannot parse reference")
} }
// Retrieve remote digest through HEAD request // Retrieve remote digest through HEAD request
rmDigest, err := docker.GetDigest(ctx, c.sysCtx, rmRef) rmDigest, err := docker.GetDigest(ctx, c.sysCtx, rmRef)
if err != nil { if err != nil {
return Manifest{}, false, errors.Wrap(err, "Cannot get image digest from HEAD request") return Manifest{}, false, errors.Wrap(err, "cannot get image digest from HEAD request")
} }
// Digest match, returns db manifest // Digest match, returns db manifest
@@ -48,13 +48,13 @@ func (c *Client) Manifest(image Image, dbManifest Manifest) (Manifest, bool, err
rmCloser, err := rmRef.NewImage(ctx, c.sysCtx) rmCloser, err := rmRef.NewImage(ctx, c.sysCtx)
if err != nil { if err != nil {
return Manifest{}, false, errors.Wrap(err, "Cannot create image closer") return Manifest{}, false, errors.Wrap(err, "cannot create image closer")
} }
defer rmCloser.Close() defer rmCloser.Close()
rmRawManifest, rmManifestMimeType, err := rmCloser.Manifest(ctx) rmRawManifest, rmManifestMimeType, err := rmCloser.Manifest(ctx)
if err != nil { if err != nil {
return Manifest{}, false, errors.Wrap(err, "Cannot get raw manifest") return Manifest{}, false, errors.Wrap(err, "cannot get raw manifest")
} }
// For manifests list compare also digest matching the platform // For manifests list compare also digest matching the platform
@@ -62,19 +62,19 @@ func (c *Client) Manifest(image Image, dbManifest Manifest) (Manifest, bool, err
if c.opts.CompareDigest && len(dbManifest.Raw) > 0 && dbManifest.isManifestList() && isManifestList(rmManifestMimeType) { if c.opts.CompareDigest && len(dbManifest.Raw) > 0 && dbManifest.isManifestList() && isManifestList(rmManifestMimeType) {
dbManifestList, err := manifest.ListFromBlob(dbManifest.Raw, dbManifest.MIMEType) dbManifestList, err := manifest.ListFromBlob(dbManifest.Raw, dbManifest.MIMEType)
if err != nil { if err != nil {
return Manifest{}, false, errors.Wrap(err, "Cannot parse manifest list") return Manifest{}, false, errors.Wrap(err, "cannot parse manifest list")
} }
dbManifestPlatformDigest, err := dbManifestList.ChooseInstance(c.sysCtx) dbManifestPlatformDigest, err := dbManifestList.ChooseInstance(c.sysCtx)
if err != nil { if err != nil {
return Manifest{}, false, errors.Wrapf(err, "Error choosing image instance") return Manifest{}, false, errors.Wrapf(err, "error choosing image instance")
} }
rmManifestList, err := manifest.ListFromBlob(rmRawManifest, rmManifestMimeType) rmManifestList, err := manifest.ListFromBlob(rmRawManifest, rmManifestMimeType)
if err != nil { if err != nil {
return Manifest{}, false, errors.Wrap(err, "Cannot parse manifest list") return Manifest{}, false, errors.Wrap(err, "cannot parse manifest list")
} }
rmManifestPlatformDigest, err := rmManifestList.ChooseInstance(c.sysCtx) rmManifestPlatformDigest, err := rmManifestList.ChooseInstance(c.sysCtx)
if err != nil { if err != nil {
return Manifest{}, false, errors.Wrapf(err, "Error choosing image instance") return Manifest{}, false, errors.Wrapf(err, "error choosing image instance")
} }
updated = dbManifestPlatformDigest != rmManifestPlatformDigest updated = dbManifestPlatformDigest != rmManifestPlatformDigest
} }
@@ -82,7 +82,7 @@ func (c *Client) Manifest(image Image, dbManifest Manifest) (Manifest, bool, err
// Metadata describing the Docker image // Metadata describing the Docker image
rmInspect, err := rmCloser.Inspect(ctx) rmInspect, err := rmCloser.Inspect(ctx)
if err != nil { if err != nil {
return Manifest{}, false, errors.Wrap(err, "Cannot inspect") return Manifest{}, false, errors.Wrap(err, "cannot inspect")
} }
rmTag := rmInspect.Tag rmTag := rmInspect.Tag
if len(rmTag) == 0 { if len(rmTag) == 0 {

View File

@@ -36,7 +36,7 @@ func (c *Client) Tags(opts TagsOptions) (*Tags, error) {
imgRef, err := ParseReference(opts.Image.String()) imgRef, err := ParseReference(opts.Image.String())
if err != nil { if err != nil {
return nil, errors.Wrap(err, "Cannot parse reference") return nil, errors.Wrap(err, "cannot parse reference")
} }
tags, err := docker.GetRepositoryTags(ctx, c.sysCtx, imgRef) tags, err := docker.GetRepositoryTags(ctx, c.sysCtx, imgRef)