Skip to content

Commit 78de930

Browse files
anik120perdasilva
authored andcommitted
(catsrc) add custom unmarshaller for registry poll interval (openshift#169)
This PR implements a custom unmarshaller for the CatalogSourceSpec.UpdateStrategy. When a value that cannot be unmarshalled to *metav1.Duration was being passed to UpdateStrategy.RegistryPoll.Interval, the catalogSource sync loops in the catalog operator was stuck in an infinite loop instead of backing off and performing other syncs. The custom unmarshaller tries to unmarshall the input to the field to *metav1.Duration, and if it fails, it sets a default value of 15m for the field. Otherwise it accepts the value passed to the field. Upstream-repository: api Upstream-commit: 7c97612f258921a973da23fb55dcd77892401be4
1 parent 798c757 commit 78de930

File tree

3 files changed

+135
-6
lines changed

3 files changed

+135
-6
lines changed

staging/api/pkg/operators/v1alpha1/catalogsource_types.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package v1alpha1
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"github.com/sirupsen/logrus"
67
corev1 "k8s.io/api/core/v1"
@@ -10,8 +11,9 @@ import (
1011
)
1112

1213
const (
13-
CatalogSourceCRDAPIVersion = GroupName + "/" + GroupVersion
14-
CatalogSourceKind = "CatalogSource"
14+
CatalogSourceCRDAPIVersion = GroupName + "/" + GroupVersion
15+
CatalogSourceKind = "CatalogSource"
16+
DefaultRegistryPollDuration = 15 * time.Minute
1517
)
1618

1719
// SourceType indicates the type of backing store for a CatalogSource
@@ -36,6 +38,8 @@ const (
3638
CatalogSourceConfigMapError ConditionReason = "ConfigMapError"
3739
// CatalogSourceRegistryServerError denotes when there is an issue querying the specified registry server.
3840
CatalogSourceRegistryServerError ConditionReason = "RegistryServerError"
41+
// CatalogSourceIntervalInvalidError denotes if the registry polling interval is invalid.
42+
CatalogSourceIntervalInvalidError ConditionReason = "InvalidIntervalError"
3943
)
4044

4145
type CatalogSourceSpec struct {
@@ -119,7 +123,32 @@ type RegistryPoll struct {
119123
// Interval is used to determine the time interval between checks of the latest catalog source version.
120124
// The catalog operator polls to see if a new version of the catalog source is available.
121125
// If available, the latest image is pulled and gRPC traffic is directed to the latest catalog source.
122-
Interval *metav1.Duration `json:"interval,omitempty"`
126+
RawInterval string `json:"interval,omitempty"`
127+
Interval *metav1.Duration `json:"-"`
128+
ParsingError string `json:"-"`
129+
}
130+
131+
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
132+
func (u *UpdateStrategy) UnmarshalJSON(data []byte) (err error) {
133+
type alias struct {
134+
*RegistryPoll `json:"registryPoll,omitempty"`
135+
}
136+
us := alias{}
137+
if err = json.Unmarshal(data, &us); err != nil {
138+
return err
139+
}
140+
registryPoll := &RegistryPoll{
141+
RawInterval: us.RegistryPoll.RawInterval,
142+
}
143+
duration, err := time.ParseDuration(registryPoll.RawInterval)
144+
if err != nil {
145+
registryPoll.ParsingError = fmt.Sprintf("error parsing spec.updateStrategy.registryPoll.interval. Using the default value of %s instead. Error: %s", DefaultRegistryPollDuration, err)
146+
registryPoll.Interval = &metav1.Duration{Duration: DefaultRegistryPollDuration}
147+
} else {
148+
registryPoll.Interval = &metav1.Duration{Duration: duration}
149+
}
150+
u.RegistryPoll = registryPoll
151+
return nil
123152
}
124153

125154
type RegistryServiceStatus struct {

staging/api/pkg/operators/v1alpha1/types_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package v1alpha1
22

33
import (
4+
"encoding/json"
5+
"fmt"
46
"sort"
57
"testing"
68
"time"
@@ -208,3 +210,72 @@ func TestCatalogSource_Poll(t *testing.T) {
208210
require.Equal(t, tt.result, table[i].catsrc.Poll(), table[i].description)
209211
}
210212
}
213+
214+
func TestUpdateStrategyUnmarshal(t *testing.T) {
215+
type TestStruct struct {
216+
UpdateStrategy UpdateStrategy `json:"updateStrategy,omitempty"`
217+
}
218+
validDuration, err := time.ParseDuration("45m")
219+
if err != nil {
220+
panic(fmt.Errorf("error parsing duration: %s", err))
221+
}
222+
defaultDuration, err := time.ParseDuration("15m")
223+
if err != nil {
224+
panic(fmt.Errorf("error parsing duration: %s", err))
225+
}
226+
tests := []struct {
227+
name string
228+
in []byte
229+
out TestStruct
230+
err error
231+
}{
232+
{
233+
name: "valid",
234+
in: []byte(`{"UpdateStrategy": {"registryPoll":{"interval":"45m"}}}`),
235+
out: TestStruct{
236+
UpdateStrategy{
237+
&RegistryPoll{
238+
RawInterval: "45m",
239+
Interval: &metav1.Duration{Duration: validDuration},
240+
ParsingError: "",
241+
},
242+
},
243+
},
244+
},
245+
{
246+
name: "invalid",
247+
in: []byte(`{"UpdateStrategy": {"registryPoll":{"interval":"19mError Code"}}}`),
248+
out: TestStruct{
249+
UpdateStrategy{
250+
&RegistryPoll{
251+
RawInterval: "19mError Code",
252+
Interval: &metav1.Duration{Duration: defaultDuration},
253+
ParsingError: "error parsing spec.updateStrategy.registryPoll.interval. Using the default value of 15m0s instead. Error: time: unknown unit \"mError Code\" in duration \"19mError Code\"",
254+
},
255+
},
256+
},
257+
},
258+
{
259+
name: "empty",
260+
in: []byte(`{"UpdateStrategy": {"registryPoll":{"interval":""}}}`),
261+
out: TestStruct{
262+
UpdateStrategy{
263+
&RegistryPoll{
264+
Interval: &metav1.Duration{Duration: defaultDuration},
265+
ParsingError: "error parsing spec.updateStrategy.registryPoll.interval. Using the default value of 15m0s instead. Error: time: invalid duration \"\"",
266+
},
267+
},
268+
},
269+
},
270+
}
271+
for _, tt := range tests {
272+
t.Run(tt.name, func(t *testing.T) {
273+
s := TestStruct{}
274+
err := json.Unmarshal(tt.in, &s)
275+
require.Equal(t, tt.out.UpdateStrategy.RawInterval, s.UpdateStrategy.RawInterval)
276+
require.Equal(t, tt.out.UpdateStrategy.Interval, s.UpdateStrategy.Interval)
277+
require.Equal(t, tt.out.UpdateStrategy.ParsingError, s.UpdateStrategy.ParsingError)
278+
require.Equal(t, tt.err, err)
279+
})
280+
}
281+
}

vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/catalogsource_types.go

Lines changed: 32 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)