diff --git a/cmd/cli.go b/cmd/cli.go index 16e60a2a..874a1f6d 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -24,7 +24,7 @@ type CliGlobals struct { // BeforeApply is a hook that run cli cmd are executed. func (s *CliGlobals) BeforeApply() (err error) { - s.conn, err = grpc.Dial(s.GRPCAuthority, grpc.WithTransportCredentials(insecure.NewCredentials())) + s.conn, err = grpc.NewClient(s.GRPCAuthority, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return err } diff --git a/cmd/image.go b/cmd/image.go index da7c91ee..7d608d7b 100644 --- a/cmd/image.go +++ b/cmd/image.go @@ -9,9 +9,9 @@ import ( "time" "unicode" - "github.com/AlecAivazis/survey/v2" + survey "github.com/AlecAivazis/survey/v2" "github.com/crazy-max/diun/v4/pb" - "github.com/docker/go-units" + units "github.com/docker/go-units" "github.com/jedib0t/go-pretty/v6/table" "github.com/tidwall/pretty" "google.golang.org/protobuf/encoding/protojson" @@ -138,8 +138,8 @@ func (s *ImageRemoveCmd) Run(_ *Context) error { // ImagePruneCmd holds image prune command type ImagePruneCmd struct { CliGlobals - //All bool `kong:"name='all',default='false',help='Remove all manifests from the database.'"` - //Filter string `kong:"name='filter',help='Provide filter values (e.g., until=24h).'"` + // All bool `kong:"name='all',default='false',help='Remove all manifests from the database.'"` + // Filter string `kong:"name='filter',help='Provide filter values (e.g., until=24h).'"` Force bool `kong:"name='force',default='false',help='Do not prompt for confirmation.'"` } diff --git a/cmd/main.go b/cmd/main.go index 6e59a76d..43c65ad6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -39,7 +39,7 @@ func main() { Author: "CrazyMax", Version: version, } - meta.UserAgent = fmt.Sprintf("%s/%s go/%s %s", meta.ID, meta.Version, runtime.Version()[2:], strings.Title(runtime.GOOS)) + meta.UserAgent = fmt.Sprintf("%s/%s go/%s %s", meta.ID, meta.Version, runtime.Version()[2:], strings.Title(runtime.GOOS)) //nolint:staticcheck // ignoring "SA1019: strings.Title is deprecated", as for our use we don't need full unicode support if meta.Hostname, err = os.Hostname(); err != nil { log.Fatal().Err(err).Msg("Cannot resolve hostname") } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 5b70440f..0c0b7add 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -189,7 +189,7 @@ for {{ .Entry.Manifest.Platform }} platform. }, }, RegOpts: model.RegOpts{ - { + model.RegOpt{ Name: "myregistry", Selector: model.RegOptSelectorName, Username: "fii", @@ -197,7 +197,7 @@ for {{ .Entry.Manifest.Platform }} platform. InsecureTLS: utl.NewFalse(), Timeout: utl.NewDuration(5 * time.Second), }, - { + model.RegOpt{ Name: "docker.io", Selector: model.RegOptSelectorImage, Username: "foo", @@ -205,7 +205,7 @@ for {{ .Entry.Manifest.Platform }} platform. InsecureTLS: utl.NewFalse(), Timeout: utl.NewDuration(0), }, - { + model.RegOpt{ Name: "docker.io/crazymax", Selector: model.RegOptSelectorImage, UsernameFile: "./fixtures/run_secrets_username", @@ -301,7 +301,7 @@ func TestLoadEnv(t *testing.T) { Watch: (&model.Watch{}).GetDefaults(), Defaults: (&model.Defaults{}).GetDefaults(), RegOpts: model.RegOpts{ - { + model.RegOpt{ Name: "docker.io", Selector: model.RegOptSelectorImage, UsernameFile: "./fixtures/run_secrets_username", diff --git a/internal/notif/client.go b/internal/notif/client.go index 4ddc915e..f18622b9 100644 --- a/internal/notif/client.go +++ b/internal/notif/client.go @@ -99,7 +99,7 @@ func (c *Client) Send(entry model.NotifEntry) { for _, n := range c.notifiers { log.Debug().Str("image", entry.Image.String()).Msgf("Sending %s notification...", n.Name()) if err := n.Send(entry); err != nil { - log.Error().Err(err).Str("image", entry.Image.String()).Msgf("%s notification failed", strings.Title(n.Name())) + log.Error().Err(err).Str("image", entry.Image.String()).Msgf("%s notification failed", strings.Title(n.Name())) //nolint:staticcheck // ignoring "SA1019: strings.Title is deprecated", as for our use we don't need full unicode support } } } diff --git a/internal/notif/discord/client.go b/internal/notif/discord/client.go index 20966d82..bb07b5a8 100644 --- a/internal/notif/discord/client.go +++ b/internal/notif/discord/client.go @@ -2,6 +2,7 @@ package discord import ( "bytes" + "context" "encoding/json" "fmt" "net/http" @@ -41,10 +42,6 @@ func (c *Client) Name() string { func (c *Client) Send(entry model.NotifEntry) error { var content bytes.Buffer - hc := http.Client{ - Timeout: *c.cfg.Timeout, - } - message, err := msg.New(msg.Options{ Meta: c.meta, Entry: entry, @@ -125,7 +122,11 @@ func (c *Client) Send(entry model.NotifEntry) error { return err } - req, err := http.NewRequest("POST", u.String(), dataBuf) + hc := http.Client{} + ctx, cancel := context.WithTimeout(context.Background(), *c.cfg.Timeout) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "POST", u.String(), dataBuf) if err != nil { return err } @@ -137,6 +138,7 @@ func (c *Client) Send(entry model.NotifEntry) error { if err != nil { return err } + defer resp.Body.Close() if resp.StatusCode != http.StatusNoContent { return errors.Errorf("unexpected HTTP status %d: %s", resp.StatusCode, resp.Body) diff --git a/internal/notif/gotify/client.go b/internal/notif/gotify/client.go index 94222c5c..094ebe7f 100644 --- a/internal/notif/gotify/client.go +++ b/internal/notif/gotify/client.go @@ -2,6 +2,7 @@ package gotify import ( "bytes" + "context" "encoding/json" "net/http" "net/url" @@ -44,10 +45,6 @@ func (c *Client) Send(entry model.NotifEntry) error { return errors.Wrap(err, "cannot retrieve token secret for Gotify notifier") } - hc := http.Client{ - Timeout: *c.cfg.Timeout, - } - message, err := msg.New(msg.Options{ Meta: c.meta, Entry: entry, @@ -92,7 +89,11 @@ func (c *Client) Send(entry model.NotifEntry) error { q.Set("token", token) u.RawQuery = q.Encode() - req, err := http.NewRequest("POST", u.String(), bytes.NewBuffer(jsonBody)) + hc := http.Client{} + ctx, cancel := context.WithTimeout(context.Background(), *c.cfg.Timeout) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "POST", u.String(), bytes.NewBuffer(jsonBody)) if err != nil { return err } @@ -105,6 +106,7 @@ func (c *Client) Send(entry model.NotifEntry) error { if err != nil { return err } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { var errBody struct { diff --git a/internal/notif/ntfy/client.go b/internal/notif/ntfy/client.go index 0884484c..c664f0de 100644 --- a/internal/notif/ntfy/client.go +++ b/internal/notif/ntfy/client.go @@ -2,6 +2,7 @@ package ntfy import ( "bytes" + "context" "encoding/json" "fmt" "net/http" @@ -38,10 +39,6 @@ func (c *Client) Name() string { // Send creates and sends a ntfy notification with an entry func (c *Client) Send(entry model.NotifEntry) error { - hc := http.Client{ - Timeout: *c.cfg.Timeout, - } - message, err := msg.New(msg.Options{ Meta: c.meta, Entry: entry, @@ -84,7 +81,11 @@ func (c *Client) Send(entry model.NotifEntry) error { q := u.Query() u.RawQuery = q.Encode() - req, err := http.NewRequest("POST", u.String(), dataBuf) + hc := http.Client{} + ctx, cancel := context.WithTimeout(context.Background(), *c.cfg.Timeout) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "POST", u.String(), dataBuf) if err != nil { return err } @@ -99,6 +100,7 @@ func (c *Client) Send(entry model.NotifEntry) error { if err != nil { return err } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { var errBody struct { diff --git a/internal/notif/rocketchat/client.go b/internal/notif/rocketchat/client.go index 4db4aaf3..40d65fc2 100644 --- a/internal/notif/rocketchat/client.go +++ b/internal/notif/rocketchat/client.go @@ -2,6 +2,7 @@ package rocketchat import ( "bytes" + "context" "encoding/json" "net/http" "net/url" @@ -46,10 +47,6 @@ func (c *Client) Send(entry model.NotifEntry) error { return errors.Wrap(err, "cannot retrieve token secret for RocketChat notifier") } - hc := http.Client{ - Timeout: *c.cfg.Timeout, - } - message, err := msg.New(msg.Options{ Meta: c.meta, Entry: entry, @@ -125,7 +122,11 @@ func (c *Client) Send(entry model.NotifEntry) error { } u.Path = path.Join(u.Path, "api/v1/chat.postMessage") - req, err := http.NewRequest("POST", u.String(), dataBuf) + hc := http.Client{} + ctx, cancel := context.WithTimeout(context.Background(), *c.cfg.Timeout) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "POST", u.String(), dataBuf) if err != nil { return err } @@ -139,6 +140,7 @@ func (c *Client) Send(entry model.NotifEntry) error { if err != nil { return err } + defer resp.Body.Close() var respBody struct { Success bool `json:"success"` diff --git a/internal/notif/script/client.go b/internal/notif/script/client.go index bbdd1ccb..32094f2b 100644 --- a/internal/notif/script/client.go +++ b/internal/notif/script/client.go @@ -66,6 +66,6 @@ func (c *Client) Send(entry model.NotifEntry) error { return errors.Wrap(err, strings.TrimSpace(stderr.String())) } - log.Debug().Msgf(strings.TrimSpace(stdout.String())) + log.Debug().Msg(strings.TrimSpace(stdout.String())) return nil } diff --git a/internal/notif/signalrest/client.go b/internal/notif/signalrest/client.go index 405a0d41..c1b37f74 100644 --- a/internal/notif/signalrest/client.go +++ b/internal/notif/signalrest/client.go @@ -2,6 +2,7 @@ package signalrest import ( "bytes" + "context" "encoding/json" "net/http" @@ -34,10 +35,6 @@ func (c *Client) Name() string { // Send creates and sends a signalrest notification with an entry func (c *Client) Send(entry model.NotifEntry) error { - hc := http.Client{ - Timeout: *c.cfg.Timeout, - } - message, err := msg.New(msg.Options{ Meta: c.meta, Entry: entry, @@ -65,7 +62,11 @@ func (c *Client) Send(entry model.NotifEntry) error { return err } - req, err := http.NewRequest("POST", c.cfg.Endpoint, bytes.NewBuffer(body)) + hc := http.Client{} + ctx, cancel := context.WithTimeout(context.Background(), *c.cfg.Timeout) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "POST", c.cfg.Endpoint, bytes.NewBuffer(body)) if err != nil { return err } @@ -79,6 +80,11 @@ func (c *Client) Send(entry model.NotifEntry) error { req.Header.Set("Content-Type", "application/json") req.Header.Set("User-Agent", c.meta.UserAgent) - _, err = hc.Do(req) - return err + resp, err := hc.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + return nil } diff --git a/internal/notif/teams/client.go b/internal/notif/teams/client.go index 1efbd60b..bbaa5470 100644 --- a/internal/notif/teams/client.go +++ b/internal/notif/teams/client.go @@ -2,6 +2,7 @@ package teams import ( "bytes" + "context" "encoding/json" "fmt" "net/http" @@ -49,10 +50,6 @@ type Fact struct { // Send creates and sends a webhook notification with an entry func (c *Client) Send(entry model.NotifEntry) error { - hc := http.Client{ - Timeout: time.Duration(10) * time.Second, - } - message, err := msg.New(msg.Options{ Meta: c.meta, Entry: entry, @@ -106,7 +103,11 @@ func (c *Client) Send(entry model.NotifEntry) error { return err } - req, err := http.NewRequest("POST", c.cfg.WebhookURL, bytes.NewBuffer(jsonBody)) + hc := http.Client{} + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(10)*time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "POST", c.cfg.WebhookURL, bytes.NewBuffer(jsonBody)) if err != nil { return err } @@ -114,6 +115,11 @@ func (c *Client) Send(entry model.NotifEntry) error { req.Header.Add("Content-Type", "application/json") req.Header.Set("User-Agent", c.meta.UserAgent) - _, err = hc.Do(req) - return err + resp, err := hc.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + return nil } diff --git a/internal/notif/webhook/client.go b/internal/notif/webhook/client.go index e73d37c1..c6b4de13 100644 --- a/internal/notif/webhook/client.go +++ b/internal/notif/webhook/client.go @@ -2,6 +2,7 @@ package webhook import ( "bytes" + "context" "net/http" "github.com/crazy-max/diun/v4/internal/model" @@ -33,10 +34,6 @@ func (c *Client) Name() string { // Send creates and sends a webhook notification with an entry func (c *Client) Send(entry model.NotifEntry) error { - hc := http.Client{ - Timeout: *c.cfg.Timeout, - } - message, err := msg.New(msg.Options{ Meta: c.meta, Entry: entry, @@ -50,7 +47,11 @@ func (c *Client) Send(entry model.NotifEntry) error { return err } - req, err := http.NewRequest(c.cfg.Method, c.cfg.Endpoint, bytes.NewBuffer(body)) + hc := http.Client{} + ctx, cancel := context.WithTimeout(context.Background(), *c.cfg.Timeout) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "POST", c.cfg.Endpoint, bytes.NewBuffer(body)) if err != nil { return err } @@ -63,6 +64,11 @@ func (c *Client) Send(entry model.NotifEntry) error { req.Header.Set("User-Agent", c.meta.UserAgent) - _, err = hc.Do(req) - return err + resp, err := hc.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + return nil } diff --git a/internal/provider/common_test.go b/internal/provider/common_test.go index 72a9abe3..0982d69c 100644 --- a/internal/provider/common_test.go +++ b/internal/provider/common_test.go @@ -7,7 +7,7 @@ import ( "github.com/crazy-max/diun/v4/pkg/registry" "github.com/crazy-max/diun/v4/pkg/utl" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestValidateImage(t *testing.T) { @@ -693,10 +693,10 @@ func TestValidateImage(t *testing.T) { tt.defaults, ) if tt.expectedErr == nil { - assert.NoError(t, err) - assert.Equal(t, tt.expectedImage, img) + require.NoError(t, err) + require.Equal(t, tt.expectedImage, img) } else { - assert.ErrorContains(t, err, tt.expectedErr.Error()) + require.ErrorContains(t, err, tt.expectedErr.Error()) } }) } diff --git a/pkg/registry/image.go b/pkg/registry/image.go index e8dd9f76..a44e1eb8 100644 --- a/pkg/registry/image.go +++ b/pkg/registry/image.go @@ -85,13 +85,6 @@ func (i Image) Reference() string { return i.Tag } -// WithDigest sets the digest for an image. -func (i Image) WithDigest(digest digest.Digest) (err error) { - i.Digest = digest - i.named, err = reference.WithDigest(i.named, digest) - return err -} - func (i Image) hubLink() (string, error) { if i.opts.HubTpl != "" { var out bytes.Buffer diff --git a/pkg/registry/manifest_test.go b/pkg/registry/manifest_test.go index b3f2574b..7274ee83 100644 --- a/pkg/registry/manifest_test.go +++ b/pkg/registry/manifest_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestCompareDigest(t *testing.T) { @@ -26,7 +27,7 @@ func TestCompareDigest(t *testing.T) { // download manifest _, _, err = rc.Manifest(img, Manifest{}) - assert.NoError(t, err) + require.NoError(t, err) // check manifest manifest, _, err := rc.Manifest(img, Manifest{ @@ -36,7 +37,7 @@ func TestCompareDigest(t *testing.T) { Digest: "sha256:db618981ef3d07699ff6cd8b9d2a81f51a021747bc08c85c1b0e8d11130c2be5", Platform: "linux/amd64", }) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "docker.io/crazymax/diun", manifest.Name) assert.Equal(t, "2.5.0", manifest.Tag) assert.Equal(t, "application/vnd.docker.distribution.manifest.list.v2+json", manifest.MIMEType) @@ -64,7 +65,7 @@ func TestManifest(t *testing.T) { // download manifest _, _, err = rc.Manifest(img, Manifest{}) - assert.NoError(t, err) + require.NoError(t, err) // check manifest manifest, updated, err := rc.Manifest(img, Manifest{ @@ -101,7 +102,7 @@ func TestManifest(t *testing.T) { }`), }) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, false, updated) assert.Equal(t, "docker.io/portainer/portainer-ce", manifest.Name) assert.Equal(t, "linux-amd64-2.5.1", manifest.Tag) @@ -130,7 +131,7 @@ func TestManifestMultiUpdatedPlatform(t *testing.T) { // download manifest _, _, err = rc.Manifest(img, Manifest{}) - assert.NoError(t, err) + require.NoError(t, err) // check manifest manifest, updated, err := rc.Manifest(img, Manifest{ @@ -186,7 +187,7 @@ func TestManifestMultiUpdatedPlatform(t *testing.T) { }`), }) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, true, updated) assert.Equal(t, "docker.io/library/mongo", manifest.Name) assert.Equal(t, "3.6.21", manifest.Tag) @@ -215,7 +216,7 @@ func TestManifestMultiNotUpdatedPlatform(t *testing.T) { // download manifest _, _, err = rc.Manifest(img, Manifest{}) - assert.NoError(t, err) + require.NoError(t, err) // check manifest manifest, updated, err := rc.Manifest(img, Manifest{ @@ -271,7 +272,7 @@ func TestManifestMultiNotUpdatedPlatform(t *testing.T) { }`), }) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, false, updated) assert.Equal(t, "docker.io/library/mongo", manifest.Name) assert.Equal(t, "3.6.21", manifest.Tag) @@ -299,7 +300,7 @@ func TestManifestVariant(t *testing.T) { } manifest, _, err := rc.Manifest(img, Manifest{}) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, "docker.io/crazymax/diun", manifest.Name) assert.Equal(t, "2.5.0", manifest.Tag) assert.Equal(t, "application/vnd.docker.distribution.manifest.list.v2+json", manifest.MIMEType) @@ -327,11 +328,11 @@ func TestManifestTaggedDigest(t *testing.T) { // download manifest _, _, err = rc.Manifest(img, Manifest{}) - assert.NoError(t, err) + require.NoError(t, err) // check manifest manifest, updated, err := rc.Manifest(img, manifestCrazymaxDiun4250) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, false, updated) assert.Equal(t, "docker.io/crazymax/diun", manifest.Name) assert.Equal(t, "4.25.0", manifest.Tag) @@ -359,7 +360,7 @@ func TestManifestTaggedDigestUnknownTag(t *testing.T) { } _, _, err = rc.Manifest(img, Manifest{}) - assert.Error(t, err) + require.Error(t, err) } var manifestCrazymaxDiun4250 = Manifest{ diff --git a/pkg/registry/ref.go b/pkg/registry/ref.go index 49330f5b..f6286074 100644 --- a/pkg/registry/ref.go +++ b/pkg/registry/ref.go @@ -28,9 +28,7 @@ func namedReference(name string) (reference.Named, error) { ref, err := reference.ParseNormalizedNamed(name) if err != nil { return nil, errors.Wrapf(err, "parsing normalized named %q", name) - } - - if _, ok := ref.(reference.Named); !ok { + } else if ref == nil { return nil, errors.Errorf("%q is not a named reference", name) } diff --git a/pkg/registry/tags_test.go b/pkg/registry/tags_test.go index 9174a879..ecd452be 100644 --- a/pkg/registry/tags_test.go +++ b/pkg/registry/tags_test.go @@ -23,8 +23,8 @@ func TestTags(t *testing.T) { t.Error(err) } - assert.True(t, tags.Total > 0) - assert.True(t, len(tags.List) > 0) + assert.Greater(t, tags.Total, 0) + assert.Greater(t, len(tags.List), 0) } func TestTagsWithDigest(t *testing.T) { @@ -46,8 +46,8 @@ func TestTagsWithDigest(t *testing.T) { t.Error(err) } - assert.True(t, tags.Total > 0) - assert.True(t, len(tags.List) > 0) + assert.Greater(t, tags.Total, 0) + assert.Greater(t, len(tags.List), 0) } func TestTagsSort(t *testing.T) {