@@ -18,6 +18,7 @@ package cel
1818
1919import (
2020 "context"
21+ "fmt"
2122
2223 "github.com/fluxcd/cli-utils/pkg/kstatus/polling/engine"
2324 "github.com/fluxcd/cli-utils/pkg/kstatus/polling/event"
@@ -27,45 +28,85 @@ import (
2728 "k8s.io/apimachinery/pkg/api/meta"
2829 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2930 "k8s.io/apimachinery/pkg/runtime/schema"
31+
32+ "github.com/fluxcd/pkg/apis/kustomize"
3033)
3134
3235// StatusReader implements the engine.StatusReader interface for a specific GroupKind and
3336// set of healthcheck expressions.
3437type StatusReader struct {
35- genericStatusReader engine. StatusReader
36- gk schema.GroupKind
38+ mapper meta. RESTMapper
39+ evaluators map [ schema.GroupKind ] * StatusEvaluator
3740}
3841
3942// NewStatusReader returns a new StatusReader for the given GroupKind and healthcheck expressions.
40- // The context is used to control the execution of the underlying operations performed by the
41- // the reader.
42- func NewStatusReader (ctx context.Context , mapper meta.RESTMapper , gk schema.GroupKind ,
43- se * StatusEvaluator ) engine.StatusReader {
44-
45- statusFunc := func (u * unstructured.Unstructured ) (* status.Result , error ) {
46- return se .Evaluate (ctx , u )
43+ func NewStatusReader (healthchecks []kustomize.CustomHealthCheck ) (func (meta.RESTMapper ) engine.StatusReader , error ) {
44+ // Build evaluators map.
45+ evaluators := make (map [schema.GroupKind ]* StatusEvaluator , len (healthchecks ))
46+ for i , hc := range healthchecks {
47+ gk := schema .FromAPIVersionAndKind (hc .APIVersion , hc .Kind ).GroupKind ()
48+ if _ , ok := evaluators [gk ]; ok {
49+ return nil , fmt .Errorf (
50+ "duplicate custom health check for GroupKind %s at healthchecks[%d]" , gk .String (), i )
51+ }
52+ se , err := NewStatusEvaluator (& hc .HealthCheckExpressions )
53+ if err != nil {
54+ return nil , fmt .Errorf (
55+ "failed to create custom status evaluator for healthchecks[%d]: %w" , i , err )
56+ }
57+ evaluators [gk ] = se
4758 }
4859
49- genericStatusReader := kstatusreaders .NewGenericStatusReader (mapper , statusFunc )
50- return & StatusReader {
51- genericStatusReader : genericStatusReader ,
52- gk : gk ,
53- }
60+ return func (mapper meta.RESTMapper ) engine.StatusReader {
61+ return & StatusReader {
62+ mapper : mapper ,
63+ evaluators : evaluators ,
64+ }
65+ }, nil
5466}
5567
5668// Supports returns true if the StatusReader supports the given GroupKind.
5769func (g * StatusReader ) Supports (gk schema.GroupKind ) bool {
58- return gk == g .gk
70+ _ , ok := g .evaluators [gk ]
71+ return ok
5972}
6073
6174// ReadStatus reads the status of the resource with the given metadata.
6275func (g * StatusReader ) ReadStatus (ctx context.Context , reader engine.ClusterReader ,
6376 resource object.ObjMetadata ) (* event.ResourceStatus , error ) {
64- return g .genericStatusReader .ReadStatus (ctx , reader , resource )
77+
78+ if ! g .Supports (resource .GroupKind ) {
79+ return nil , fmt .Errorf ("the GroupKind %s is not supported" , resource .GroupKind .String ())
80+ }
81+
82+ return g .genericStatusReader (ctx , resource .GroupKind ).ReadStatus (ctx , reader , resource )
6583}
6684
6785// ReadStatusForObject reads the status of the given resource.
6886func (g * StatusReader ) ReadStatusForObject (ctx context.Context , reader engine.ClusterReader ,
6987 resource * unstructured.Unstructured ) (* event.ResourceStatus , error ) {
70- return g .genericStatusReader .ReadStatusForObject (ctx , reader , resource )
88+
89+ // Compute GroupKind.
90+ apiVersion , ok , _ := unstructured .NestedFieldCopy (resource .Object , "apiVersion" )
91+ if ! ok {
92+ return nil , fmt .Errorf ("resource is missing apiVersion field" )
93+ }
94+ kind , ok , _ := unstructured .NestedFieldCopy (resource .Object , "kind" )
95+ if ! ok {
96+ return nil , fmt .Errorf ("resource is missing kind field" )
97+ }
98+ gk := schema .FromAPIVersionAndKind (apiVersion .(string ), kind .(string )).GroupKind ()
99+ if ! g .Supports (gk ) {
100+ return nil , fmt .Errorf ("the GroupKind %s is not supported" , gk .String ())
101+ }
102+
103+ return g .genericStatusReader (ctx , gk ).ReadStatusForObject (ctx , reader , resource )
104+ }
105+
106+ // genericStatusReader returns the underlying generic status reader.
107+ func (g * StatusReader ) genericStatusReader (ctx context.Context , gk schema.GroupKind ) engine.StatusReader {
108+ statusFunc := func (u * unstructured.Unstructured ) (* status.Result , error ) {
109+ return g .evaluators [gk ].Evaluate (ctx , u )
110+ }
111+ return kstatusreaders .NewGenericStatusReader (g .mapper , statusFunc )
71112}
0 commit comments