diff --git a/pkg/controller/operators/decorators/operator.go b/pkg/controller/operators/decorators/operator.go index 5a21320e6f..1eef37acae 100644 --- a/pkg/controller/operators/decorators/operator.go +++ b/pkg/controller/operators/decorators/operator.go @@ -4,6 +4,7 @@ import ( "fmt" "sort" "strings" + "sync" "github.com/go-viper/mapstructure/v2" "github.com/itchyny/gojq" @@ -32,19 +33,26 @@ const ( ) var ( - csvGVK = operatorsv1alpha1.SchemeGroupVersion.WithKind(operatorsv1alpha1.ClusterServiceVersionKind) - componentConditionsJQ *gojq.Query - csvConditionsJQ *gojq.Query + csvGVK = operatorsv1alpha1.SchemeGroupVersion.WithKind(operatorsv1alpha1.ClusterServiceVersionKind) + + jqOnce sync.Once + jqErr error + componentConditions *gojq.Query + csvConditions *gojq.Query ) -func init() { - var err error - if componentConditionsJQ, err = gojq.Parse(".status.conditions"); err != nil { - panic(fmt.Errorf("failed to parse component conditions jq: %s", err)) - } - if csvConditionsJQ, err = gojq.Parse(".status | [{\"type\": .phase, \"status\": \"True\", \"reason\": .reason, \"message\": .message, \"lastUpdateTime\": .lastUpdateTime,\"lastTransitionTime\": .lastTransitionTime}]"); err != nil { - panic(fmt.Errorf("failed to parse csv conditions jq: %s", err)) - } +func jqQueries() (componentQ, csvQ *gojq.Query, err error) { + jqOnce.Do(func() { + if componentConditions, jqErr = gojq.Parse(".status.conditions"); jqErr != nil { + jqErr = fmt.Errorf("failed to parse component conditions jq: %w", jqErr) + return + } + if csvConditions, jqErr = gojq.Parse(".status | [{\"type\": .phase, \"status\": \"True\", \"reason\": .reason, \"message\": .message, \"lastUpdateTime\": .lastUpdateTime,\"lastTransitionTime\": .lastTransitionTime}]"); jqErr != nil { + jqErr = fmt.Errorf("failed to parse csv conditions jq: %w", jqErr) + return + } + }) + return componentConditions, csvConditions, jqErr } // OperatorNames returns a list of operator names extracted from the given labels. @@ -413,10 +421,16 @@ func (c *Component) Reference() (ref *operatorsv1.RichReference, err error) { ObjectReference: truncated, } - query := componentConditionsJQ + componentQ, csvQ, qErr := jqQueries() + if qErr != nil { + err = qErr + return + } + + query := componentQ switch c.GroupVersionKind() { case csvGVK: - query = csvConditionsJQ + query = csvQ } iter := query.Run(c.UnstructuredContent()) diff --git a/pkg/controller/operators/decorators/operator_test.go b/pkg/controller/operators/decorators/operator_test.go index eb99b7e9a0..22e8556319 100644 --- a/pkg/controller/operators/decorators/operator_test.go +++ b/pkg/controller/operators/decorators/operator_test.go @@ -14,6 +14,18 @@ import ( operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" ) +func TestJQQueries(t *testing.T) { + componentQ, csvQ, err := jqQueries() + require.NoError(t, err) + require.NotNil(t, componentQ) + require.NotNil(t, csvQ) + + componentQ2, csvQ2, err2 := jqQueries() + require.NoError(t, err2) + require.Same(t, componentQ, componentQ2, "sync.Once should return the same pointer") + require.Same(t, csvQ, csvQ2, "sync.Once should return the same pointer") +} + func TestOperatorNames(t *testing.T) { type args struct { labels map[string]string