From 8ba2e926a0a2381fb27f8119d549029d10ef9a0a Mon Sep 17 00:00:00 2001 From: Parthiba-Hazra Date: Tue, 23 Apr 2024 20:51:49 +0530 Subject: [PATCH] Refactoring newly added pull policy - removing interval pull policy as we can cover 95% of use cases using the hourly, daily, weekly pull policies and also removing the pruning interval command as it will set to be constant 7 days. Signed-off-by: Parthiba-Hazra --- cmd/cmd.go | 2 +- go.mod | 18 +- go.sum | 20 +-- internal/commands/config_prune_interval.go | 102 ----------- .../commands/config_prune_interval_test.go | 170 ------------------ pkg/image/fetcher.go | 19 +- pkg/image/fetcher_test.go | 5 +- pkg/image/pull_policy.go | 132 ++------------ pkg/image/pull_policy_test.go | 2 +- pkg/testmocks/mock_image_fetcher.go | 83 +-------- .../mock_image_pull_policy_handler.go | 137 ++++++++++++++ 11 files changed, 190 insertions(+), 500 deletions(-) delete mode 100644 internal/commands/config_prune_interval.go delete mode 100644 internal/commands/config_prune_interval_test.go create mode 100644 pkg/testmocks/mock_image_pull_policy_handler.go diff --git a/cmd/cmd.go b/cmd/cmd.go index 7108b81af..adcb5de82 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -139,7 +139,7 @@ func initConfig() (config.Config, string, error) { return cfg, path, nil } -func initClient(logger logging.Logger, cfg config.Config) (*client.Client, error) { +func initClient(logger logging.Logger, cfg config.Config, imagePullPolicyHandler image.ImagePullPolicyHandler) (*client.Client, error) { if err := client.ProcessDockerContext(logger); err != nil { return nil, err } diff --git a/go.mod b/go.mod index 019c2e026..f860f5c85 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/docker/docker v25.0.5+incompatible github.com/docker/go-connections v0.5.0 github.com/dustin/go-humanize v1.0.1 - github.com/gdamore/tcell/v2 v2.7.4 + github.com/gdamore/tcell/v2 v2.7.1 github.com/go-git/go-git/v5 v5.11.0 github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.6.0 @@ -29,9 +29,9 @@ require ( github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 github.com/sclevine/spec v1.4.0 github.com/spf13/cobra v1.8.0 - golang.org/x/crypto v0.21.0 - golang.org/x/mod v0.16.0 - golang.org/x/oauth2 v0.18.0 + golang.org/x/crypto v0.20.0 + golang.org/x/mod v0.15.0 + golang.org/x/oauth2 v0.17.0 golang.org/x/sync v0.6.0 golang.org/x/sys v0.18.0 golang.org/x/term v0.18.0 @@ -128,11 +128,11 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/vbatts/tar-split v0.11.5 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect - go.opentelemetry.io/otel v1.22.0 // indirect - go.opentelemetry.io/otel/metric v1.22.0 // indirect - go.opentelemetry.io/otel/trace v1.22.0 // indirect - golang.org/x/net v0.22.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect + go.opentelemetry.io/otel v1.23.0 // indirect + go.opentelemetry.io/otel/metric v1.23.0 // indirect + go.opentelemetry.io/otel/trace v1.23.0 // indirect + golang.org/x/net v0.21.0 // indirect golang.org/x/tools v0.18.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/go.sum b/go.sum index b78983320..17ca08fc8 100644 --- a/go.sum +++ b/go.sum @@ -154,8 +154,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04= -github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU= -github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg= +github.com/gdamore/tcell/v2 v2.7.1 h1:TiCcmpWHiAU7F0rA2I3S2Y4mmLmO9KHxJ7E1QhYzQbc= +github.com/gdamore/tcell/v2 v2.7.1/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -417,15 +417,15 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= +golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -441,11 +441,11 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/internal/commands/config_prune_interval.go b/internal/commands/config_prune_interval.go deleted file mode 100644 index 5e00527e5..000000000 --- a/internal/commands/config_prune_interval.go +++ /dev/null @@ -1,102 +0,0 @@ -package commands - -import ( - "fmt" - "regexp" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "github.com/buildpacks/pack/internal/config" - "github.com/buildpacks/pack/internal/style" - "github.com/buildpacks/pack/pkg/image" - "github.com/buildpacks/pack/pkg/logging" -) - -func ConfigPruneInterval(logger logging.Logger, cfg config.Config, cfgPath string, imagePullPolicyHandler image.ImagePullPolicyHandler) *cobra.Command { - var unset bool - var intervalRegex = regexp.MustCompile(`^(\d+d)?(\d+h)?(\d+m)?$`) - - cmd := &cobra.Command{ - Use: "prune-interval", - Args: cobra.MaximumNArgs(1), - Short: "List, set, and unset the global pruning interval used for cleaning up outdated image entries from $HOME/.pack/image.json file", - Long: "You can use this command to list, set, and unset the default pruning interval for cleaning up unused images:\n" + - "* To list your current pruning interval, run `pack config prune-interval`.\n" + - "* To set a new pruning interval, run `pack config prune-interval ` where is a duration string (e.g., '7d' for 7 days).\n" + - "* To unset the pruning interval, run `pack config prune-interval --unset`.\n" + - fmt.Sprintf("Unsetting the pruning interval will reset the interval to the default, which is %s.", style.Symbol("7 days")), - RunE: logError(logger, func(cmd *cobra.Command, args []string) error { - imageJSONPath, err := image.DefaultImageJSONPath() - if err != nil { - return err - } - - switch { - case unset: - if len(args) > 0 { - return errors.Errorf("prune interval and --unset cannot be specified simultaneously") - } - imageJSON, err := imagePullPolicyHandler.Read(imageJSONPath) - if err != nil { - return err - } - oldPruneInterval := imageJSON.Interval.PruningInterval - imageJSON.Interval.PruningInterval = "7d" - - err = imagePullPolicyHandler.Write(imageJSON, imageJSONPath) - if err != nil { - return err - } - logger.Infof("Successfully unset pruning interval %s", style.Symbol(oldPruneInterval)) - logger.Infof("Pruning interval has been set to %s", style.Symbol(imageJSON.Interval.PruningInterval)) - case len(args) == 0: // list - imageJSON, err := imagePullPolicyHandler.Read(imageJSONPath) - if err != nil { - return err - } - pruneInterval := imageJSON.Interval.PruningInterval - if err != nil { - return err - } - - logger.Infof("The current prune interval is %s", style.Symbol(pruneInterval)) - default: // set - newPruneInterval := args[0] - - imageJSON, err := imagePullPolicyHandler.Read(imageJSONPath) - if err != nil { - return err - } - pruneInterval := imageJSON.Interval.PruningInterval - if err != nil { - return err - } - - if newPruneInterval == pruneInterval { - logger.Infof("Prune Interval is already set to %s", style.Symbol(newPruneInterval)) - return nil - } - - matches := intervalRegex.FindStringSubmatch(newPruneInterval) - if len(matches) == 0 { - return errors.Errorf("invalid interval format: %s", newPruneInterval) - } - - imageJSON.Interval.PruningInterval = newPruneInterval - - err = imagePullPolicyHandler.Write(imageJSON, imageJSONPath) - if err != nil { - return err - } - - logger.Infof("Successfully set %s as the pruning interval", style.Symbol(imageJSON.Interval.PruningInterval)) - } - - return nil - }), - } - cmd.Flags().BoolVarP(&unset, "unset", "u", false, "Unset prune interval, and set it back to the default prune-interval, which is "+style.Symbol("7d")) - AddHelpFlag(cmd, "prune-interval") - return cmd -} diff --git a/internal/commands/config_prune_interval_test.go b/internal/commands/config_prune_interval_test.go deleted file mode 100644 index 0f0d7a4b8..000000000 --- a/internal/commands/config_prune_interval_test.go +++ /dev/null @@ -1,170 +0,0 @@ -package commands_test - -import ( - "bytes" - "os" - "path/filepath" - "testing" - - "github.com/sclevine/spec" - "github.com/sclevine/spec/report" - "github.com/spf13/cobra" - - "github.com/buildpacks/pack/internal/commands" - "github.com/buildpacks/pack/internal/config" - "github.com/buildpacks/pack/pkg/image" - "github.com/buildpacks/pack/pkg/logging" - fetcher_mock "github.com/buildpacks/pack/pkg/testmocks" - h "github.com/buildpacks/pack/testhelpers" -) - -var imageJSON *image.ImageJSON - -func TestConfigPruneInterval(t *testing.T) { - spec.Run(t, "ConfigPruneIntervalCommand", testConfigPruneIntervalCommand, spec.Random(), spec.Report(report.Terminal{})) -} - -func testConfigPruneIntervalCommand(t *testing.T, when spec.G, it spec.S) { - var ( - command *cobra.Command - logger logging.Logger - outBuf bytes.Buffer - tempPackHome string - configFile string - assert = h.NewAssertionManager(t) - cfg = config.Config{} - imagePullPolicyHandler *fetcher_mock.MockImagePullPolicyHandler - ) - - it.Before(func() { - logger = logging.NewLogWithWriters(&outBuf, &outBuf) - imagePullPolicyHandler = fetcher_mock.NewMockPullPolicyManager(logger) - tempPackHome, _ = os.MkdirTemp("", "pack-home") - configFile = filepath.Join(tempPackHome, "config.toml") - - command = commands.ConfigPruneInterval(logger, cfg, configFile, imagePullPolicyHandler) - }) - - it.After(func() { - _ = os.RemoveAll(tempPackHome) - }) - - when("#ConfigPruneInterval", func() { - when("no arguments are provided", func() { - it.Before(func() { - interval := "5d" - command.SetArgs([]string{interval}) - - err := command.Execute() - assert.Nil(err) - }) - - it.After(func() { - command.SetArgs([]string{"--unset"}) - - err := command.Execute() - assert.Nil(err) - }) - - it("lists the current pruning interval", func() { - command.SetArgs([]string{}) - - err := command.Execute() - - assert.Nil(err) - assert.Contains(outBuf.String(), "The current prune interval is") - }) - }) - - when("an argument is provided", func() { - when("argument is valid", func() { - it.After(func() { - command.SetArgs([]string{"--unset"}) - - err := command.Execute() - assert.Nil(err) - }) - - it("sets the provided interval as the pruning interval", func() { - interval := "5d" - command.SetArgs([]string{interval}) - - err := command.Execute() - - assert.Nil(err) - assert.Contains(outBuf.String(), "Successfully set") - }) - }) - - when("argument is invalid", func() { - it("returns an error", func() { - interval := "invalid" - command.SetArgs([]string{interval}) - - err := command.Execute() - - assert.Error(err) - assert.Contains(err.Error(), "invalid interval format") - }) - }) - - when("argument is valid and the same as the already set pruning interval", func() { - it.Before(func() { - imageJSON = &image.ImageJSON{ - Interval: &image.Interval{ - PullingInterval: "7d", - PruningInterval: "5d", - LastPrune: "2023-01-01T00:00:00Z", - }, - Image: &image.ImageData{ - ImageIDtoTIME: map[string]string{}, - }, - } - - imagePullPolicyHandler.MockRead = func(path string) (*image.ImageJSON, error) { - return imageJSON, nil - } - }) - - it.After(func() { - command.SetArgs([]string{"--unset"}) - - err := command.Execute() - assert.Nil(err) - imagePullPolicyHandler.MockRead = nil - }) - - it("sets the provided interval as the pruning interval", func() { - interval := "5d" - command.SetArgs([]string{interval}) - - err := command.Execute() - assert.Nil(err) - assert.Contains(outBuf.String(), "Prune Interval is already set to") - }) - }) - }) - - when("--unset flag is provided", func() { - it("unsets the pruning interval", func() { - command.SetArgs([]string{"--unset"}) - - err := command.Execute() - - assert.Nil(err) - assert.Contains(outBuf.String(), "Successfully unset pruning interval") - }) - }) - - when("both interval and --unset flag are provided", func() { - it("returns an error", func() { - command.SetArgs([]string{"5d", "--unset"}) - - err := command.Execute() - - assert.Error(err) - assert.Contains(err.Error(), "prune interval and --unset cannot be specified simultaneously") - }) - }) - }) -} diff --git a/pkg/image/fetcher.go b/pkg/image/fetcher.go index 2afad2444..339824e26 100644 --- a/pkg/image/fetcher.go +++ b/pkg/image/fetcher.go @@ -38,16 +38,16 @@ type LayoutOption struct { type ImagePullPolicyHandler interface { ParsePullPolicy(policy string) (PullPolicy, error) - CheckImagePullInterval(imageID string, path string) (bool, error) + CheckImagePullInterval(imageID string, path string, pullPolicy PullPolicy) (bool, error) PruneOldImages(docker DockerClient) error UpdateImagePullRecord(path string, imageID string, timestamp string) error - UpdateImageJSONDuration(intervalStr string) error + GetDuration(p PullPolicy) time.Duration Read(path string) (*ImageJSON, error) Write(imageJSON *ImageJSON, path string) error } func intervalPolicy(options FetchOptions) bool { - return options.PullPolicy == PullWithInterval || options.PullPolicy == PullHourly || options.PullPolicy == PullDaily || options.PullPolicy == PullWeekly + return options.PullPolicy == PullHourly || options.PullPolicy == PullDaily || options.PullPolicy == PullWeekly } func NewPullPolicyManager(logger logging.Logger) ImagePullPolicyHandler { @@ -132,8 +132,8 @@ func (f *Fetcher) Fetch(ctx context.Context, name string, options FetchOptions) if err == nil || !errors.Is(err, ErrNotFound) { return img, err } - case PullWithInterval, PullDaily, PullHourly, PullWeekly: - pull, err := f.imagePullChecker.CheckImagePullInterval(name, imageJSONpath) + case PullDaily, PullHourly, PullWeekly: + pull, err := f.imagePullChecker.CheckImagePullInterval(name, imageJSONpath, options.PullPolicy) if err != nil { f.logger.Warnf("failed to check pulling interval for image %s, %s", name, err) } @@ -326,7 +326,7 @@ func (i *ImagePullPolicyManager) UpdateImagePullRecord(path string, imageID stri return nil } -func (i *ImagePullPolicyManager) CheckImagePullInterval(imageID string, path string) (bool, error) { +func (i *ImagePullPolicyManager) CheckImagePullInterval(imageID string, path string, pullPolicy PullPolicy) (bool, error) { imageJSON, err := i.Read(path) if err != nil { return false, err @@ -343,12 +343,7 @@ func (i *ImagePullPolicyManager) CheckImagePullInterval(imageID string, path str return false, errors.Wrap(err, "failed to parse image timestamp from JSON") } - durationStr := imageJSON.Interval.PullingInterval - - duration, err := parseDurationString(durationStr) - if err != nil { - return false, errors.Wrap(err, "failed to parse duration from JSON") - } + duration := i.GetDuration(pullPolicy) timeThreshold := time.Now().Add(-duration) diff --git a/pkg/image/fetcher_test.go b/pkg/image/fetcher_test.go index bedcba701..bb3a0b621 100644 --- a/pkg/image/fetcher_test.go +++ b/pkg/image/fetcher_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/buildpacks/imgutil" + "github.com/golang/mock/gomock" "github.com/buildpacks/imgutil/local" "github.com/buildpacks/imgutil/remote" @@ -30,7 +31,9 @@ var docker client.CommonAPIClient var logger logging.Logger var registryConfig *h.TestRegistryConfig var imageJSON *image.ImageJSON -var mockImagePullPolicyHandler = testmocks.NewMockPullPolicyManager(logger) +var mockController *gomock.Controller + +var mockImagePullPolicyHandler = testmocks.NewMockImagePullPolicyHandler(mockController) func TestFetcher(t *testing.T) { color.Disable(true) diff --git a/pkg/image/pull_policy.go b/pkg/image/pull_policy.go index 1d9a47bff..eeb0aede0 100644 --- a/pkg/image/pull_policy.go +++ b/pkg/image/pull_policy.go @@ -2,12 +2,8 @@ package image import ( "encoding/json" - "fmt" "os" "path/filepath" - "regexp" - "strconv" - "strings" "time" "github.com/pkg/errors" @@ -21,19 +17,17 @@ import ( // PullPolicy defines a policy for how to manage images type PullPolicy int -var interval string +const ( + PRUNE_TIME = 7 * 24 * 60 * time.Minute + HOURLY = 1 * 60 * time.Minute + DAILY = 1 * 24 * 60 * time.Minute + WEEKLY = 7 * 24 * 60 * time.Minute +) type ImagePullPolicyManager struct { Logger logging.Logger } -var ( - hourly = "1h" - daily = "1d" - weekly = "7d" - intervalRegex = regexp.MustCompile(`^(\d+d)?(\d+h)?(\d+m)?$`) -) - const ( // Always pull images, even if they are present PullAlways PullPolicy = iota @@ -47,14 +41,10 @@ const ( PullNever // PullIfNotPresent pulls images if they aren't present PullIfNotPresent - // PullWithInterval pulls images with specified intervals - PullWithInterval ) type Interval struct { - PullingInterval string `json:"pulling_interval"` - PruningInterval string `json:"pruning_interval"` - LastPrune string `json:"last_prune"` + LastPrune string `json:"last_prune"` } type ImageData struct { @@ -79,44 +69,9 @@ var nameMap = map[string]PullPolicy{"always": PullAlways, "hourly": PullHourly, // ParsePullPolicy from string with support for interval formats func (i *ImagePullPolicyManager) ParsePullPolicy(policy string) (PullPolicy, error) { if val, ok := nameMap[policy]; ok { - if val == PullHourly { - err := i.UpdateImageJSONDuration(hourly) - if err != nil { - return PullAlways, err - } - } - if val == PullDaily { - err := i.UpdateImageJSONDuration(daily) - if err != nil { - return PullAlways, err - } - } - if val == PullWeekly { - err := i.UpdateImageJSONDuration(weekly) - if err != nil { - return PullAlways, err - } - } - return val, nil } - if strings.HasPrefix(policy, "interval=") { - interval = policy - intervalStr := strings.TrimPrefix(policy, "interval=") - matches := intervalRegex.FindStringSubmatch(intervalStr) - if len(matches) == 0 { - return PullAlways, errors.Errorf("invalid interval format: %s", intervalStr) - } - - err := i.UpdateImageJSONDuration(intervalStr) - if err != nil { - return PullAlways, err - } - - return PullWithInterval, nil - } - return PullAlways, errors.Errorf("invalid pull policy %s", policy) } @@ -134,58 +89,22 @@ func (p PullPolicy) String() string { return "never" case PullIfNotPresent: return "if-not-present" - case PullWithInterval: - return fmt.Sprintf("%v", interval) } return "" } -func (i *ImagePullPolicyManager) UpdateImageJSONDuration(intervalStr string) error { - path, err := DefaultImageJSONPath() - if err != nil { - return err - } - - imageJSON, err := i.Read(path) - if err != nil { - return err - } - - imageJSON.Interval.PullingInterval = intervalStr - - return i.Write(imageJSON, path) -} - -func parseDurationString(durationStr string) (time.Duration, error) { - var totalMinutes int - for i := 0; i < len(durationStr); { - endIndex := i + 1 - for endIndex < len(durationStr) && durationStr[endIndex] >= '0' && durationStr[endIndex] <= '9' { - endIndex++ - } - - value, err := strconv.Atoi(durationStr[i:endIndex]) - if err != nil { - return 0, errors.Wrapf(err, "invalid interval format: %s", durationStr) - } - unit := durationStr[endIndex] - - switch unit { - case 'd': - totalMinutes += value * 24 * 60 - case 'h': - totalMinutes += value * 60 - case 'm': - totalMinutes += value - default: - return 0, errors.Errorf("invalid interval uniit: %s", string(unit)) - } - - i = endIndex + 1 +func (i *ImagePullPolicyManager) GetDuration(p PullPolicy) time.Duration { + switch p { + case PullHourly: + return HOURLY + case PullDaily: + return DAILY + case PullWeekly: + return WEEKLY } - return time.Duration(totalMinutes) * time.Minute, nil + return 0 } func (i *ImagePullPolicyManager) PruneOldImages(docker DockerClient) error { @@ -204,24 +123,13 @@ func (i *ImagePullPolicyManager) PruneOldImages(docker DockerClient) error { return errors.Wrap(err, "failed to parse last prune timestamp from JSON") } - pruningInterval, err := parseDurationString(imageJSON.Interval.PruningInterval) - if err != nil { - return errors.Wrap(err, "failed to parse pruning interval from JSON") - } - - if time.Since(lastPruneTime) < pruningInterval { + if time.Since(lastPruneTime) < PRUNE_TIME { // not enough time has passed since the last prune return nil } } - // prune images older than the pruning interval - pruningDuration, err := parseDurationString(imageJSON.Interval.PruningInterval) - if err != nil { - return errors.Wrap(err, "failed to parse pruning interval from JSON") - } - - pruningThreshold := time.Now().Add(-pruningDuration) + pruningThreshold := time.Now().Add(-PRUNE_TIME) for imageID, timestamp := range imageJSON.Image.ImageIDtoTIME { imageTimestamp, err := time.Parse(time.RFC3339, timestamp) @@ -256,9 +164,7 @@ func (i *ImagePullPolicyManager) Read(path string) (*ImageJSON, error) { if _, err := os.Stat(path); os.IsNotExist(err) { return &ImageJSON{ Interval: &Interval{ - PullingInterval: "7d", - PruningInterval: "7d", - LastPrune: "", + LastPrune: "", }, Image: &ImageData{}, }, nil diff --git a/pkg/image/pull_policy_test.go b/pkg/image/pull_policy_test.go index 6fe10fca6..265c99231 100644 --- a/pkg/image/pull_policy_test.go +++ b/pkg/image/pull_policy_test.go @@ -27,7 +27,7 @@ func testPullPolicy(t *testing.T, when spec.G, it spec.S) { it.Before(func() { logger = logging.NewLogWithWriters(&outBuf, &outBuf) - imagePullChecker = fetcher_mock.NewMockPullPolicyManager(logger) + imagePullChecker = fetcher_mock.NewMockPullPolicyManager(mockController) }) it("returns PullNever for never", func() { diff --git a/pkg/testmocks/mock_image_fetcher.go b/pkg/testmocks/mock_image_fetcher.go index 90225529e..a82a54a9d 100644 --- a/pkg/testmocks/mock_image_fetcher.go +++ b/pkg/testmocks/mock_image_fetcher.go @@ -1,7 +1,7 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/buildpacks/pack/pkg/client (interfaces: ImageFetcher) -// Package testmocks is a generated GoMock package. +// Package mock_client is a generated GoMock package. package testmocks import ( @@ -9,15 +9,10 @@ import ( reflect "reflect" imgutil "github.com/buildpacks/imgutil" - gomock "github.com/golang/mock/gomock" - - "github.com/buildpacks/pack/pkg/logging" - image "github.com/buildpacks/pack/pkg/image" + gomock "github.com/golang/mock/gomock" ) -var imageJSON *image.ImageJSON - // MockImageFetcher is a mock of ImageFetcher interface. type MockImageFetcher struct { ctrl *gomock.Controller @@ -29,17 +24,6 @@ type MockImageFetcherMockRecorder struct { mock *MockImageFetcher } -type MockImagePullPolicyHandler struct { - *image.ImagePullPolicyManager - MockParsePullPolicy func(policy string, logger logging.Logger) (image.PullPolicy, error) - MockCheckImagePullInterval func(imageID string, path string) (bool, error) - MockPruneOldImages func(docker *image.DockerClient) error - MockUpdateImagePullRecord func(path string, imageID string, timestamp string) error - MockUpdateImageJSONDuration func(intervalStr string) error - MockRead func(path string) (*image.ImageJSON, error) - MockWrite func(imageJSON *image.ImageJSON, path string) error -} - // NewMockImageFetcher creates a new mock instance. func NewMockImageFetcher(ctrl *gomock.Controller) *MockImageFetcher { mock := &MockImageFetcher{ctrl: ctrl} @@ -66,66 +50,3 @@ func (mr *MockImageFetcherMockRecorder) Fetch(arg0, arg1, arg2 interface{}) *gom mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fetch", reflect.TypeOf((*MockImageFetcher)(nil).Fetch), arg0, arg1, arg2) } - -func NewMockPullPolicyManager(logger logging.Logger) *MockImagePullPolicyHandler { - return &MockImagePullPolicyHandler{} -} - -func (m *MockImagePullPolicyHandler) CheckImagePullInterval(imageID string, path string) (bool, error) { - if m.MockCheckImagePullInterval != nil { - return m.MockCheckImagePullInterval(imageID, path) - } - return false, nil -} - -func (m *MockImagePullPolicyHandler) Write(imageJSON *image.ImageJSON, path string) error { - if m.MockWrite != nil { - return m.MockWrite(imageJSON, path) - } - return nil -} - -func (m *MockImagePullPolicyHandler) Read(path string) (*image.ImageJSON, error) { - if m.MockRead != nil { - return m.MockRead(path) - } - - imageJSON = &image.ImageJSON{ - Interval: &image.Interval{ - PullingInterval: "7d", - PruningInterval: "7d", - LastPrune: "2023-01-01T00:00:00Z", - }, - Image: &image.ImageData{ - ImageIDtoTIME: map[string]string{ - "repoName": "2023-01-01T00:00:00Z", - }, - }, - } - - return imageJSON, nil -} - -func (m *MockImagePullPolicyHandler) PruneOldImages(docker image.DockerClient) error { - if m.MockPruneOldImages != nil { - return m.MockPruneOldImages(&docker) - } - - return m.ImagePullPolicyManager.PruneOldImages(docker) -} - -func (m *MockImagePullPolicyHandler) UpdateImagePullRecord(path string, imageID string, timestamp string) error { - if m.MockUpdateImagePullRecord != nil { - return m.MockUpdateImagePullRecord(path, imageID, timestamp) - } - - return nil -} - -func (m *MockImagePullPolicyHandler) UpdateImageJSONDuration(intervalStr string) error { - if m.MockUpdateImageJSONDuration != nil { - return m.MockUpdateImageJSONDuration(intervalStr) - } - - return nil -} diff --git a/pkg/testmocks/mock_image_pull_policy_handler.go b/pkg/testmocks/mock_image_pull_policy_handler.go new file mode 100644 index 000000000..4d72fd1e1 --- /dev/null +++ b/pkg/testmocks/mock_image_pull_policy_handler.go @@ -0,0 +1,137 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/buildpacks/pack/pkg/image (interfaces: ImagePullPolicyHandler) + +// Package mock_image is a generated GoMock package. +package testmocks + +import ( + reflect "reflect" + time "time" + + image "github.com/buildpacks/pack/pkg/image" + gomock "github.com/golang/mock/gomock" +) + +// MockImagePullPolicyHandler is a mock of ImagePullPolicyHandler interface. +type MockImagePullPolicyHandler struct { + ctrl *gomock.Controller + recorder *MockImagePullPolicyHandlerMockRecorder +} + +// MockImagePullPolicyHandlerMockRecorder is the mock recorder for MockImagePullPolicyHandler. +type MockImagePullPolicyHandlerMockRecorder struct { + mock *MockImagePullPolicyHandler +} + +// NewMockImagePullPolicyHandler creates a new mock instance. +func NewMockImagePullPolicyHandler(ctrl *gomock.Controller) *MockImagePullPolicyHandler { + mock := &MockImagePullPolicyHandler{ctrl: ctrl} + mock.recorder = &MockImagePullPolicyHandlerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockImagePullPolicyHandler) EXPECT() *MockImagePullPolicyHandlerMockRecorder { + return m.recorder +} + +// CheckImagePullInterval mocks base method. +func (m *MockImagePullPolicyHandler) CheckImagePullInterval(arg0, arg1 string, arg2 image.PullPolicy) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CheckImagePullInterval", arg0, arg1, arg2) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CheckImagePullInterval indicates an expected call of CheckImagePullInterval. +func (mr *MockImagePullPolicyHandlerMockRecorder) CheckImagePullInterval(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckImagePullInterval", reflect.TypeOf((*MockImagePullPolicyHandler)(nil).CheckImagePullInterval), arg0, arg1, arg2) +} + +// GetDuration mocks base method. +func (m *MockImagePullPolicyHandler) GetDuration(arg0 image.PullPolicy) time.Duration { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDuration", arg0) + ret0, _ := ret[0].(time.Duration) + return ret0 +} + +// GetDuration indicates an expected call of GetDuration. +func (mr *MockImagePullPolicyHandlerMockRecorder) GetDuration(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDuration", reflect.TypeOf((*MockImagePullPolicyHandler)(nil).GetDuration), arg0) +} + +// ParsePullPolicy mocks base method. +func (m *MockImagePullPolicyHandler) ParsePullPolicy(arg0 string) (image.PullPolicy, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParsePullPolicy", arg0) + ret0, _ := ret[0].(image.PullPolicy) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParsePullPolicy indicates an expected call of ParsePullPolicy. +func (mr *MockImagePullPolicyHandlerMockRecorder) ParsePullPolicy(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParsePullPolicy", reflect.TypeOf((*MockImagePullPolicyHandler)(nil).ParsePullPolicy), arg0) +} + +// PruneOldImages mocks base method. +func (m *MockImagePullPolicyHandler) PruneOldImages(arg0 image.DockerClient) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PruneOldImages", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// PruneOldImages indicates an expected call of PruneOldImages. +func (mr *MockImagePullPolicyHandlerMockRecorder) PruneOldImages(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PruneOldImages", reflect.TypeOf((*MockImagePullPolicyHandler)(nil).PruneOldImages), arg0) +} + +// Read mocks base method. +func (m *MockImagePullPolicyHandler) Read(arg0 string) (*image.ImageJSON, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Read", arg0) + ret0, _ := ret[0].(*image.ImageJSON) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Read indicates an expected call of Read. +func (mr *MockImagePullPolicyHandlerMockRecorder) Read(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockImagePullPolicyHandler)(nil).Read), arg0) +} + +// UpdateImagePullRecord mocks base method. +func (m *MockImagePullPolicyHandler) UpdateImagePullRecord(arg0, arg1, arg2 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateImagePullRecord", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateImagePullRecord indicates an expected call of UpdateImagePullRecord. +func (mr *MockImagePullPolicyHandlerMockRecorder) UpdateImagePullRecord(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateImagePullRecord", reflect.TypeOf((*MockImagePullPolicyHandler)(nil).UpdateImagePullRecord), arg0, arg1, arg2) +} + +// Write mocks base method. +func (m *MockImagePullPolicyHandler) Write(arg0 *image.ImageJSON, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Write", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Write indicates an expected call of Write. +func (mr *MockImagePullPolicyHandlerMockRecorder) Write(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockImagePullPolicyHandler)(nil).Write), arg0, arg1) +}