2022-12-27 00:36:49 -08:00
package apicall
import (
2023-04-12 23:52:01 +02:00
"context"
2022-12-27 00:36:49 -08:00
"encoding/json"
"fmt"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2023-04-12 23:52:01 +02:00
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
2022-12-27 00:36:49 -08:00
"github.com/kyverno/kyverno/pkg/engine/jmespath"
"github.com/kyverno/kyverno/pkg/engine/variables"
)
type apiCall struct {
2024-03-11 09:30:29 +02:00
logger logr . Logger
jp jmespath . Interface
entry kyvernov1 . ContextEntry
jsonCtx enginecontext . Interface
executor Executor
2023-11-21 15:31:51 +05:30
}
2023-04-12 23:52:01 +02:00
func New (
2023-04-13 13:29:40 +02:00
logger logr . Logger ,
jp jmespath . Interface ,
2023-04-12 23:52:01 +02:00
entry kyvernov1 . ContextEntry ,
jsonCtx enginecontext . Interface ,
2023-06-10 11:20:34 +02:00
client ClientInterface ,
2023-11-21 15:31:51 +05:30
apiCallConfig APICallConfiguration ,
2023-04-12 23:52:01 +02:00
) ( * apiCall , error ) {
2022-12-27 00:36:49 -08:00
if entry . APICall == nil {
return nil , fmt . Errorf ( "missing APICall in context entry %v" , entry )
}
2024-03-11 09:30:29 +02:00
executor := NewExecutor ( logger , entry . Name , client , apiCallConfig )
2022-12-27 00:36:49 -08:00
return & apiCall {
2024-03-11 09:30:29 +02:00
logger : logger ,
jp : jp ,
entry : entry ,
jsonCtx : jsonCtx ,
executor : executor ,
2022-12-27 00:36:49 -08:00
} , nil
}
2023-06-26 15:31:40 +02:00
func ( a * apiCall ) FetchAndLoad ( ctx context . Context ) ( [ ] byte , error ) {
data , err := a . Fetch ( ctx )
if err != nil {
return nil , err
}
results , err := a . Store ( data )
if err != nil {
return nil , err
}
return results , nil
}
func ( a * apiCall ) Fetch ( ctx context . Context ) ( [ ] byte , error ) {
2023-04-12 23:52:01 +02:00
call , err := variables . SubstituteAllInType ( a . logger , a . jsonCtx , a . entry . APICall )
2022-12-27 00:36:49 -08:00
if err != nil {
return nil , fmt . Errorf ( "failed to substitute variables in context entry %s %s: %v" , a . entry . Name , a . entry . APICall . URLPath , err )
}
2024-03-11 09:30:29 +02:00
data , err := a . Execute ( ctx , & call . APICall )
2022-12-27 00:36:49 -08:00
if err != nil {
2025-01-24 05:54:32 +01:00
if data == nil && a . entry . APICall . Default != nil {
data = a . entry . APICall . Default . Raw
a . logger . V ( 4 ) . Info ( "failed to substitute variable data for APICall, using default value" , "default" , data , "name" , a . entry . Name , "URLPath" , a . entry . APICall . URLPath , "error" , err )
return data , nil
}
2022-12-27 00:36:49 -08:00
return nil , err
}
2023-06-26 15:31:40 +02:00
return data , nil
}
2022-12-27 00:36:49 -08:00
2023-06-26 15:31:40 +02:00
func ( a * apiCall ) Store ( data [ ] byte ) ( [ ] byte , error ) {
results , err := a . transformAndStore ( data )
2022-12-27 00:36:49 -08:00
if err != nil {
return nil , err
}
2023-06-26 15:31:40 +02:00
return results , nil
2022-12-27 00:36:49 -08:00
}
2024-03-11 09:30:29 +02:00
func ( a * apiCall ) Execute ( ctx context . Context , call * kyvernov1 . APICall ) ( [ ] byte , error ) {
return a . executor . Execute ( ctx , call )
2022-12-27 00:36:49 -08:00
}
func ( a * apiCall ) transformAndStore ( jsonData [ ] byte ) ( [ ] byte , error ) {
2025-01-24 05:54:32 +01:00
if a . entry . APICall . Default != nil {
if string ( jsonData ) == string ( a . entry . APICall . Default . Raw ) {
err := a . jsonCtx . AddContextEntry ( a . entry . Name , jsonData )
if err != nil {
return nil , fmt . Errorf ( "failed to add resource data to context entry %s: %w" , a . entry . Name , err )
}
2024-09-05 11:27:44 +05:30
return jsonData , nil
}
}
2022-12-27 00:36:49 -08:00
if a . entry . APICall . JMESPath == "" {
err := a . jsonCtx . AddContextEntry ( a . entry . Name , jsonData )
if err != nil {
2023-02-01 14:38:04 +08:00
return nil , fmt . Errorf ( "failed to add resource data to context entry %s: %w" , a . entry . Name , err )
2022-12-27 00:36:49 -08:00
}
return jsonData , nil
}
2023-04-12 23:52:01 +02:00
path , err := variables . SubstituteAll ( a . logger , a . jsonCtx , a . entry . APICall . JMESPath )
2022-12-27 00:36:49 -08:00
if err != nil {
2023-02-01 14:38:04 +08:00
return nil , fmt . Errorf ( "failed to substitute variables in context entry %s JMESPath %s: %w" , a . entry . Name , a . entry . APICall . JMESPath , err )
2022-12-27 00:36:49 -08:00
}
2023-04-13 13:29:40 +02:00
results , err := a . applyJMESPathJSON ( path . ( string ) , jsonData )
2022-12-27 00:36:49 -08:00
if err != nil {
2023-02-01 14:38:04 +08:00
return nil , fmt . Errorf ( "failed to apply JMESPath %s for context entry %s: %w" , path , a . entry . Name , err )
2022-12-27 00:36:49 -08:00
}
contextData , err := json . Marshal ( results )
if err != nil {
2023-02-01 14:38:04 +08:00
return nil , fmt . Errorf ( "failed to marshall APICall data for context entry %s: %w" , a . entry . Name , err )
2022-12-27 00:36:49 -08:00
}
err = a . jsonCtx . AddContextEntry ( a . entry . Name , contextData )
if err != nil {
2023-02-01 14:38:04 +08:00
return nil , fmt . Errorf ( "failed to add APICall results for context entry %s: %w" , a . entry . Name , err )
2022-12-27 00:36:49 -08:00
}
2023-04-12 23:52:01 +02:00
a . logger . V ( 4 ) . Info ( "added context data" , "name" , a . entry . Name , "len" , len ( contextData ) )
2022-12-27 00:36:49 -08:00
return contextData , nil
}
2023-04-13 13:29:40 +02:00
func ( a * apiCall ) applyJMESPathJSON ( jmesPath string , jsonData [ ] byte ) ( interface { } , error ) {
2022-12-27 00:36:49 -08:00
var data interface { }
err := json . Unmarshal ( jsonData , & data )
if err != nil {
2023-02-01 14:38:04 +08:00
return nil , fmt . Errorf ( "failed to unmarshal JSON: %s, error: %w" , string ( jsonData ) , err )
2022-12-27 00:36:49 -08:00
}
2023-04-13 13:29:40 +02:00
return a . jp . Search ( jmesPath , data )
2022-12-27 00:36:49 -08:00
}