11"""A Crossplane composition function."""
22
3+ import hashlib
4+
35import grpc
4- from crossplane .function import logging , resource , response
6+ from crossplane .function import logging , request , resource , response
57from crossplane .function .proto .v1 import run_function_pb2 as fnv1
68from crossplane .function .proto .v1 import run_function_pb2_grpc as grpcv1
9+ from jsonrpcclient import request_json
710
811
912class FunctionRunner (grpcv1 .FunctionRunnerService ):
@@ -27,37 +30,55 @@ async def RunFunction(
2730 endpoint = composite ["spec" ]["endpoint" ]
2831 cmds = composite ["spec" ]["cmds" ]
2932
33+ environment = resource .struct_to_dict (
34+ req .context ["apiextensions.crossplane.io/environment" ]
35+ )
36+ port , scheme , insecure_skip_tls_verify = get_envs (environment )
37+
38+ secret = request .get_required_resource (req , "eos-creds" )
39+ basic_auth = secret .get ("data" , {}).get ("basicAuth" , "" ) if secret else ""
40+
41+ jsonrpc_cfg = {
42+ "endpoint" : endpoint ,
43+ "port" : port ,
44+ "scheme" : scheme ,
45+ "basicAuth" : basic_auth ,
46+ "insecureSkipTLSVerify" : insecure_skip_tls_verify ,
47+ }
48+
3049 command_paths = walk_cmds (cmds )
3150 log .info ("Generated command paths" , count = len (command_paths ))
3251
3352 for path in command_paths :
3453 name = name_prefix + "-" + name_from_path (path )
3554
55+ jsonrpc_params = {
56+ "version" : 1 ,
57+ "format" : "json" ,
58+ "cmds" : ["enable" , "configure" , * path ],
59+ }
60+ jsonrpc = request_json ("runCmds" , params = jsonrpc_params )
61+
62+ resource_data = construct_resource_request (jsonrpc , jsonrpc_cfg )
63+
3664 resource .update (
3765 rsp .desired .resources [name ],
38- {
39- "apiVersion" : "http.crossplane.io/v1alpha2" ,
40- "kind" : "Request" ,
41- "spec" : {
42- "endpoint" : endpoint ,
43- "command" : path ,
44- },
45- },
66+ resource_data ,
4667 )
4768
4869 return rsp
4970
5071
5172def walk_cmds (cmds : dict , path : list [str ] | None = None ) -> list [list [str ]]:
73+ """walk_cmds function."""
5274 if path is None :
5375 path = []
5476
5577 results : list [list [str ]] = []
5678
57- # NOTE: keys are sorted to ensure deterministic reconciliation
5879 for cmd in sorted (cmds .keys ()):
5980 subtree = cmds [cmd ]
60- next_path = path + [ cmd ]
81+ next_path = [ * path , cmd ]
6182
6283 if subtree == {}:
6384 # Leaf → emit array
@@ -69,8 +90,42 @@ def walk_cmds(cmds: dict, path: list[str] | None = None) -> list[list[str]]:
6990 return results
7091
7192
72- import hashlib
73-
7493def name_from_path (path : list [str ]) -> str :
94+ """name_from_path function."""
7595 joined = "|" .join (path )
76- return hashlib .sha1 (joined .encode ()).hexdigest ()[:10 ]
96+ return hashlib .sha1 (joined .encode (), usedforsecurity = False ).hexdigest ()[:10 ]
97+
98+
99+ def get_envs (environment : dict ) -> tuple [int , str , bool ]:
100+ """Extract jsonrpc configuration from the environment."""
101+ if not environment :
102+ return 0 , "" , True
103+
104+ protocols_settings = environment .get ("jsonrpc" , {})
105+
106+ port = int (protocols_settings .get ("port" , 0 ))
107+ scheme = protocols_settings .get ("scheme" , "" )
108+ insecure_skip_tls_verify = bool (protocols_settings .get ("skipTlsVerify" , True ))
109+
110+ return port , scheme , insecure_skip_tls_verify
111+
112+
113+ def construct_resource_request (jsonrpc : str , config : dict ) -> dict :
114+ """Construct the resource request for the given data."""
115+ return {
116+ "apiVersion" : "http.crossplane.io/v1alpha2" ,
117+ "kind" : "Request" ,
118+ "spec" : {
119+ "forProvider" : {
120+ "insecureSkipTLSVerify" : config ["insecureSkipTLSVerify" ],
121+ "headers" : {
122+ "Accept" : ["application/json" ],
123+ "Authorization" : [f"Basic { config ['basicAuth' ]} " ],
124+ },
125+ "payload" : {
126+ "baseUrl" : f"{ config ['scheme' ]} ://{ config ['endpoint' ]} :{ config ['port' ]} " ,
127+ "body" : jsonrpc ,
128+ },
129+ },
130+ },
131+ }
0 commit comments