From 0c661c041a5ccfd4e6ab2eecdff6c463db5f5441 Mon Sep 17 00:00:00 2001 From: Jishnu Date: Mon, 11 Aug 2025 15:24:51 +0530 Subject: [PATCH 1/5] Added retry logic in code --- resource/package.go | 47 ++++++++++++++++++++++++++++++++++---------- resource/resource.go | 5 +++++ resource/validate.go | 39 ++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 10 deletions(-) diff --git a/resource/package.go b/resource/package.go index 6d393a1b..ef4eb7b6 100644 --- a/resource/package.go +++ b/resource/package.go @@ -9,13 +9,15 @@ import ( ) type Package struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - id string `json:"-" yaml:"-"` - Name string `json:"name,omitempty" yaml:"name,omitempty"` - Installed matcher `json:"installed" yaml:"installed"` - Versions matcher `json:"versions,omitempty" yaml:"versions,omitempty"` - Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` + id string `json:"-" yaml:"-"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Installed matcher `json:"installed" yaml:"installed"` + Versions matcher `json:"versions,omitempty" yaml:"versions,omitempty"` + Retry bool `json:"retry,omitempty" yaml:"retry,omitempty"` + RetryDelay int `json:"retry_delay,omitempty" yaml:"retry_delay,omitempty"` + Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } const ( @@ -45,20 +47,45 @@ func (p *Package) GetName() string { } return p.id } +func (p *Package) GetRetry() bool { return p.Retry } +func (p *Package) GetRetryDelay() int { return p.RetryDelay } func (p *Package) Validate(sys *system.System) []TestResult { ctx := context.WithValue(context.Background(), idKey{}, p.ID()) skip := p.Skip - sysPkg := sys.NewPackage(ctx, p.GetName(), sys, util.Config{}) var results []TestResult - results = append(results, ValidateValue(p, "installed", p.Installed, sysPkg.Installed, skip)) + + // Handle retry logic for installed check + if p.Retry { + results = append(results, ValidateValueWithRetry(p, "installed", p.Installed, + func() (any, error) { + sysPkg := sys.NewPackage(ctx, p.GetName(), sys, util.Config{}) + return sysPkg.Installed() + }, skip, p.RetryDelay)) + } else { + sysPkg := sys.NewPackage(ctx, p.GetName(), sys, util.Config{}) + results = append(results, ValidateValue(p, "installed", p.Installed, sysPkg.Installed, skip)) + } + if shouldSkip(results) { skip = true } + + // Handle retry logic for versions check if p.Versions != nil { - results = append(results, ValidateValue(p, "version", p.Versions, sysPkg.Versions, skip)) + if p.Retry { + results = append(results, ValidateValueWithRetry(p, "version", p.Versions, + func() (any, error) { + sysPkg := sys.NewPackage(ctx, p.GetName(), sys, util.Config{}) + return sysPkg.Versions() + }, skip, p.RetryDelay)) + } else { + sysPkg := sys.NewPackage(ctx, p.GetName(), sys, util.Config{}) + results = append(results, ValidateValue(p, "version", p.Versions, sysPkg.Versions, skip)) + } } + return results } diff --git a/resource/resource.go b/resource/resource.go index 104972ad..4662f26a 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -39,6 +39,11 @@ type ResourceRead interface { GetMeta() meta } +type Retryable interface { + GetRetry() bool + GetRetryDelay() int +} + type matcher any type meta map[string]any diff --git a/resource/validate.go b/resource/validate.go index 720c7b7d..6f714103 100644 --- a/resource/validate.go +++ b/resource/validate.go @@ -126,6 +126,45 @@ func ValidateValue(res ResourceRead, property string, expectedValue any, actual return ValidateGomegaValue(res, property, expectedValue, actual, skip) } +func ValidateValueWithRetry(res ResourceRead, property string, expectedValue any, actualFunc func() (any, error), skip bool, retryDelay int) TestResult { + if skip { + // Return skip result immediately + skipFunc := func() (any, error) { return nil, nil } + return ValidateValue(res, property, expectedValue, skipFunc, skip) + } + + maxRetries := 2 + delay := time.Duration(retryDelay) * time.Second + if delay <= 0 { + delay = 1 * time.Second // Default delay if not specified or invalid + } + + var lastResult TestResult + + for attempt := 0; attempt < maxRetries; attempt++ { + actual, err := actualFunc() + + // Create a function that returns the current result + currentFunc := func() (any, error) { return actual, err } + result := ValidateValue(res, property, expectedValue, currentFunc, skip) + lastResult = result + + // If validation passes or there's no error, return immediately + if result.Result == SUCCESS { + return result + } + + // If not the last attempt, wait before retrying + if attempt < maxRetries-1 { + fmt.Println("Retrying...") + time.Sleep(delay) + } + } + + // Return the last result after all retries exhausted + return lastResult +} + func ValidateGomegaValue(res ResourceRead, property string, expectedValue any, actual any, skip bool) TestResult { id := res.ID() title := res.GetTitle() From e8312320cb795ce0f34a15fbfec4f4a7dd62a9ed Mon Sep 17 00:00:00 2001 From: Jishnu Date: Thu, 14 Aug 2025 13:25:48 +0530 Subject: [PATCH 2/5] Retry logic update --- resource/package.go | 12 ++++++------ resource/resource.go | 2 +- resource/validate.go | 9 +++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/resource/package.go b/resource/package.go index ef4eb7b6..61a36001 100644 --- a/resource/package.go +++ b/resource/package.go @@ -15,7 +15,7 @@ type Package struct { Name string `json:"name,omitempty" yaml:"name,omitempty"` Installed matcher `json:"installed" yaml:"installed"` Versions matcher `json:"versions,omitempty" yaml:"versions,omitempty"` - Retry bool `json:"retry,omitempty" yaml:"retry,omitempty"` + RetryCount int `json:"retry_count,omitempty" yaml:"retry_count,omitempty"` RetryDelay int `json:"retry_delay,omitempty" yaml:"retry_delay,omitempty"` Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } @@ -47,7 +47,7 @@ func (p *Package) GetName() string { } return p.id } -func (p *Package) GetRetry() bool { return p.Retry } +func (p *Package) GetRetryCount() int { return p.RetryCount } func (p *Package) GetRetryDelay() int { return p.RetryDelay } func (p *Package) Validate(sys *system.System) []TestResult { @@ -57,12 +57,12 @@ func (p *Package) Validate(sys *system.System) []TestResult { var results []TestResult // Handle retry logic for installed check - if p.Retry { + if p.RetryCount > 0 { results = append(results, ValidateValueWithRetry(p, "installed", p.Installed, func() (any, error) { sysPkg := sys.NewPackage(ctx, p.GetName(), sys, util.Config{}) return sysPkg.Installed() - }, skip, p.RetryDelay)) + }, skip, p.RetryCount, p.RetryDelay)) } else { sysPkg := sys.NewPackage(ctx, p.GetName(), sys, util.Config{}) results = append(results, ValidateValue(p, "installed", p.Installed, sysPkg.Installed, skip)) @@ -74,12 +74,12 @@ func (p *Package) Validate(sys *system.System) []TestResult { // Handle retry logic for versions check if p.Versions != nil { - if p.Retry { + if p.RetryCount > 0 { results = append(results, ValidateValueWithRetry(p, "version", p.Versions, func() (any, error) { sysPkg := sys.NewPackage(ctx, p.GetName(), sys, util.Config{}) return sysPkg.Versions() - }, skip, p.RetryDelay)) + }, skip, p.RetryCount, p.RetryDelay)) } else { sysPkg := sys.NewPackage(ctx, p.GetName(), sys, util.Config{}) results = append(results, ValidateValue(p, "version", p.Versions, sysPkg.Versions, skip)) diff --git a/resource/resource.go b/resource/resource.go index 4662f26a..67f1495d 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -40,7 +40,7 @@ type ResourceRead interface { } type Retryable interface { - GetRetry() bool + GetRetryCount() int GetRetryDelay() int } diff --git a/resource/validate.go b/resource/validate.go index 6f714103..035cc247 100644 --- a/resource/validate.go +++ b/resource/validate.go @@ -126,14 +126,17 @@ func ValidateValue(res ResourceRead, property string, expectedValue any, actual return ValidateGomegaValue(res, property, expectedValue, actual, skip) } -func ValidateValueWithRetry(res ResourceRead, property string, expectedValue any, actualFunc func() (any, error), skip bool, retryDelay int) TestResult { +func ValidateValueWithRetry(res ResourceRead, property string, expectedValue any, actualFunc func() (any, error), skip bool, retryCount int, retryDelay int) TestResult { if skip { // Return skip result immediately skipFunc := func() (any, error) { return nil, nil } return ValidateValue(res, property, expectedValue, skipFunc, skip) } - maxRetries := 2 + maxRetries := retryCount + 1 + if retryCount < 0 { + maxRetries = 1 + } delay := time.Duration(retryDelay) * time.Second if delay <= 0 { delay = 1 * time.Second // Default delay if not specified or invalid @@ -149,7 +152,6 @@ func ValidateValueWithRetry(res ResourceRead, property string, expectedValue any result := ValidateValue(res, property, expectedValue, currentFunc, skip) lastResult = result - // If validation passes or there's no error, return immediately if result.Result == SUCCESS { return result } @@ -161,7 +163,6 @@ func ValidateValueWithRetry(res ResourceRead, property string, expectedValue any } } - // Return the last result after all retries exhausted return lastResult } From 0e84806d64c121b56e0688829e97d550f6621c91 Mon Sep 17 00:00:00 2001 From: j1shnu Date: Mon, 8 Sep 2025 20:46:42 +0530 Subject: [PATCH 3/5] Updated doc --- docs/gossfile.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/gossfile.md b/docs/gossfile.md index a463c2ce..ccb69bcd 100644 --- a/docs/gossfile.md +++ b/docs/gossfile.md @@ -486,6 +486,8 @@ package: versions: - 2.2.15 skip: false + retry_count: 1 # Enables retry mechanism when greater than 0 and retry number of time mentioned here + retry_delay: 180 # Delay (in seconds) before each retry ``` !!! note From 9ecc72276d0def913ba11e216f82a0b077dd2399 Mon Sep 17 00:00:00 2001 From: j1shnu Date: Thu, 11 Sep 2025 10:29:11 +0530 Subject: [PATCH 4/5] Added retry mechanism for DNS and updated related docs --- docs/gossfile.md | 12 ++++++++++-- resource/dns.go | 27 +++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/docs/gossfile.md b/docs/gossfile.md index ccb69bcd..0fe360be 100644 --- a/docs/gossfile.md +++ b/docs/gossfile.md @@ -193,6 +193,8 @@ dns: - ::1 server: 8.8.8.8 # Also supports server:port timeout: 500 # in milliseconds (Only used when server attribute is provided) + retry_count: 1 # Enables retry mechanism when greater than 0; number of additional attempts + retry_delay: 5 # Delay (in seconds) before each retry attempt ``` It is possible to validate the following types of DNS records, but requires the ```server``` attribute be set: @@ -217,6 +219,8 @@ dns: server: 208.67.222.222 addrs: - "a.dnstest.io." + retry_count: 2 + retry_delay: 5 # Validate a PTR record PTR:8.8.8.8: @@ -224,6 +228,8 @@ dns: server: 8.8.8.8 addrs: - "dns.google." + retry_count: 2 + retry_delay: 5 # Validate and SRV record SRV:_https._tcp.dnstest.io: @@ -232,6 +238,8 @@ dns: addrs: - "0 5 443 a.dnstest.io." - "10 10 443 b.dnstest.io." + retry_count: 2 + retry_delay: 5 ``` Please note that if you want `localhost` to **only** resolve `127.0.0.1` you'll need to use [Advanced Matchers](#advanced-matchers) @@ -486,8 +494,8 @@ package: versions: - 2.2.15 skip: false - retry_count: 1 # Enables retry mechanism when greater than 0 and retry number of time mentioned here - retry_delay: 180 # Delay (in seconds) before each retry + retry_count: 1 # Enables retry mechanism when greater than 0; number of additional attempts + retry_delay: 180 # Delay (in seconds) before each retry attempt ``` !!! note diff --git a/resource/dns.go b/resource/dns.go index 6cd11400..260f8ca1 100644 --- a/resource/dns.go +++ b/resource/dns.go @@ -20,6 +20,8 @@ type DNS struct { Addrs matcher `json:"addrs,omitempty" yaml:"addrs,omitempty"` Timeout int `json:"timeout" yaml:"timeout"` Server string `json:"server,omitempty" yaml:"server,omitempty"` + RetryCount int `json:"retry_count,omitempty" yaml:"retry_count,omitempty"` + RetryDelay int `json:"retry_delay,omitempty" yaml:"retry_delay,omitempty"` Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } @@ -50,6 +52,8 @@ func (d *DNS) GetResolve() string { } return d.id } +func (d *DNS) GetRetryCount() int { return d.RetryCount } +func (d *DNS) GetRetryDelay() int { return d.RetryDelay } func (d *DNS) Validate(sys *system.System) []TestResult { ctx := context.WithValue(context.Background(), idKey{}, d.ID()) @@ -58,19 +62,34 @@ func (d *DNS) Validate(sys *system.System) []TestResult { d.Timeout = 500 } - sysDNS := sys.NewDNS(ctx, d.GetResolve(), sys, util.Config{Timeout: time.Duration(d.Timeout) * time.Millisecond, Server: d.Server}) - var results []TestResult // Backwards compatibility hack for now if d.Resolvable == nil { d.Resolvable = d.Resolveable } - results = append(results, ValidateValue(d, "resolvable", d.Resolvable, sysDNS.Resolvable, skip)) + // Retry logic for resolvable + if d.RetryCount > 0 { + results = append(results, ValidateValueWithRetry(d, "resolvable", d.Resolvable, func() (any, error) { + sysDNS := sys.NewDNS(ctx, d.GetResolve(), sys, util.Config{Timeout: time.Duration(d.Timeout) * time.Millisecond, Server: d.Server}) + return sysDNS.Resolvable() + }, skip, d.RetryCount, d.RetryDelay)) + } else { + sysDNS := sys.NewDNS(ctx, d.GetResolve(), sys, util.Config{Timeout: time.Duration(d.Timeout) * time.Millisecond, Server: d.Server}) + results = append(results, ValidateValue(d, "resolvable", d.Resolvable, sysDNS.Resolvable, skip)) + } if shouldSkip(results) { skip = true } if d.Addrs != nil { - results = append(results, ValidateValue(d, "addrs", d.Addrs, sysDNS.Addrs, skip)) + if d.RetryCount > 0 { + results = append(results, ValidateValueWithRetry(d, "addrs", d.Addrs, func() (any, error) { + sysDNS := sys.NewDNS(ctx, d.GetResolve(), sys, util.Config{Timeout: time.Duration(d.Timeout) * time.Millisecond, Server: d.Server}) + return sysDNS.Addrs() + }, skip, d.RetryCount, d.RetryDelay)) + } else { + sysDNS := sys.NewDNS(ctx, d.GetResolve(), sys, util.Config{Timeout: time.Duration(d.Timeout) * time.Millisecond, Server: d.Server}) + results = append(results, ValidateValue(d, "addrs", d.Addrs, sysDNS.Addrs, skip)) + } } return results } From c705416e6d583a44eab826b5e93f305880ed6241 Mon Sep 17 00:00:00 2001 From: j1shnu Date: Mon, 20 Oct 2025 12:01:08 +0530 Subject: [PATCH 5/5] Removed unwanted printing --- resource/validate.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/resource/validate.go b/resource/validate.go index 035cc247..bcafb137 100644 --- a/resource/validate.go +++ b/resource/validate.go @@ -132,7 +132,7 @@ func ValidateValueWithRetry(res ResourceRead, property string, expectedValue any skipFunc := func() (any, error) { return nil, nil } return ValidateValue(res, property, expectedValue, skipFunc, skip) } - + maxRetries := retryCount + 1 if retryCount < 0 { maxRetries = 1 @@ -141,28 +141,27 @@ func ValidateValueWithRetry(res ResourceRead, property string, expectedValue any if delay <= 0 { delay = 1 * time.Second // Default delay if not specified or invalid } - + var lastResult TestResult - + for attempt := 0; attempt < maxRetries; attempt++ { actual, err := actualFunc() - + // Create a function that returns the current result currentFunc := func() (any, error) { return actual, err } result := ValidateValue(res, property, expectedValue, currentFunc, skip) lastResult = result - + if result.Result == SUCCESS { return result } - + // If not the last attempt, wait before retrying if attempt < maxRetries-1 { - fmt.Println("Retrying...") time.Sleep(delay) } } - + return lastResult }