1919package io .functionmesh .compute .util ;
2020
2121import com .google .gson .Gson ;
22+ import io .functionmesh .compute .MeshWorkerService ;
2223import io .functionmesh .compute .functions .models .V1alpha1Function ;
2324import io .functionmesh .compute .functions .models .V1alpha1FunctionSpec ;
2425import io .functionmesh .compute .functions .models .V1alpha1FunctionSpecGolang ;
3637import lombok .extern .slf4j .Slf4j ;
3738import org .apache .commons .lang3 .StringUtils ;
3839import org .apache .logging .log4j .util .Strings ;
40+ import org .apache .pulsar .client .admin .PulsarAdminException ;
3941import org .apache .pulsar .common .functions .ConsumerConfig ;
4042import org .apache .pulsar .common .functions .FunctionConfig ;
4143import org .apache .pulsar .common .functions .ProducerConfig ;
4244import org .apache .pulsar .common .functions .Resources ;
45+ import org .apache .pulsar .common .functions .Utils ;
4346import org .apache .pulsar .common .policies .data .ExceptionInformation ;
4447import org .apache .pulsar .common .policies .data .FunctionStatus ;
4548import org .apache .pulsar .common .util .RestException ;
4649import org .apache .pulsar .functions .proto .Function ;
4750import org .apache .pulsar .functions .proto .InstanceCommunication ;
51+ import org .apache .pulsar .functions .utils .FunctionCommon ;
4852import org .apache .pulsar .functions .utils .FunctionConfigUtils ;
4953
5054import javax .ws .rs .core .Response ;
55+ import java .io .File ;
56+ import java .io .IOException ;
57+ import java .nio .file .Files ;
5158import java .nio .file .Path ;
5259import java .nio .file .Paths ;
5360import java .util .ArrayList ;
@@ -66,7 +73,8 @@ public class FunctionsUtil {
6673
6774 public static V1alpha1Function createV1alpha1FunctionFromFunctionConfig (String kind , String group , String version
6875 , String functionName , String functionPkgUrl , FunctionConfig functionConfig
69- , Map <String , Object > customConfigs , String cluster ) {
76+ , String cluster , MeshWorkerService worker ) {
77+ Map <String , Object > customConfigs = worker .getWorkerConfig ().getFunctionsWorkerServiceCustomConfigs ();
7078 CustomRuntimeOptions customRuntimeOptions = CommonUtil .getCustomRuntimeOptions (functionConfig .getCustomRuntimeOptions ());
7179 String clusterName = CommonUtil .getClusterName (cluster , customRuntimeOptions );
7280
@@ -236,28 +244,70 @@ public static V1alpha1Function createV1alpha1FunctionFromFunctionConfig(String k
236244 // v1alpha1FunctionSpecPulsar.setAuthConfig(CommonUtil.getPulsarClusterAuthConfigMapName(clusterName));
237245 v1alpha1FunctionSpec .setPulsar (v1alpha1FunctionSpecPulsar );
238246
239- String location = String .format ("%s/%s/%s" , functionConfig .getTenant (), functionConfig .getNamespace (),
240- functionName );
241- if (StringUtils .isNotEmpty (functionPkgUrl )) {
242- location = functionPkgUrl ;
247+ // TODO: dynamic file name to function CRD
248+ String fileName = "/pulsar/function-executable" ;
249+ boolean isPkgUrlProvided = StringUtils .isNotEmpty (functionPkgUrl );
250+ File componentPackageFile = null ;
251+ try {
252+ if (isPkgUrlProvided ) {
253+ if (Utils .hasPackageTypePrefix (functionPkgUrl )) {
254+ componentPackageFile = downloadPackageFile (worker , functionPkgUrl );
255+ } else {
256+ log .warn ("get unsupported function package url {}" , functionPkgUrl );
257+ throw new IllegalArgumentException ("Function Package url is not valid. supported url (function/sink/source)" );
258+ }
259+ } else {
260+ // TODO: support upload JAR to bk
261+ throw new IllegalArgumentException ("uploading package to mesh worker service is not supported yet." );
262+ }
263+ } catch (Exception e ) {
264+ log .error ("Invalid register function request {}: {}" , functionName , e );
265+ e .printStackTrace ();
266+ throw new RestException (Response .Status .BAD_REQUEST , e .getMessage ());
267+ }
268+ Class <?>[] typeArgs = null ;
269+ if (componentPackageFile != null ) {
270+ typeArgs = extractTypeArgs (functionConfig , componentPackageFile );
243271 }
244272 if (StringUtils .isNotEmpty (functionConfig .getJar ())) {
245273 V1alpha1FunctionSpecJava v1alpha1FunctionSpecJava = new V1alpha1FunctionSpecJava ();
246- Path path = Paths .get (functionConfig .getJar ());
247- v1alpha1FunctionSpecJava .setJar (path .getFileName ().toString ());
248- v1alpha1FunctionSpecJava .setJarLocation (location );
274+ v1alpha1FunctionSpecJava .setJar (fileName );
275+ if (isPkgUrlProvided ) {
276+ v1alpha1FunctionSpecJava .setJarLocation (functionPkgUrl );
277+ }
278+ String extraDependenciesDir = "" ;
279+ if (StringUtils .isNotEmpty (worker .getFactoryConfig ().getExtraFunctionDependenciesDir ())) {
280+ if (Paths .get (worker .getFactoryConfig ().getExtraFunctionDependenciesDir ()).isAbsolute ()) {
281+ extraDependenciesDir = worker .getFactoryConfig ().getExtraFunctionDependenciesDir ();
282+ } else {
283+ extraDependenciesDir = "/pulsar/" + worker .getFactoryConfig ().getExtraFunctionDependenciesDir ();
284+ }
285+ } else {
286+ extraDependenciesDir = "/pulsar/instances/deps" ;
287+ }
288+ v1alpha1FunctionSpecJava .setExtraDependenciesDir (extraDependenciesDir );
249289 v1alpha1FunctionSpec .setJava (v1alpha1FunctionSpecJava );
290+ if (typeArgs != null ) {
291+ if (typeArgs .length == 2 && typeArgs [0 ] != null ) {
292+ v1alpha1FunctionSpecInput .setTypeClassName (typeArgs [0 ].getName ());
293+ }
294+ if (typeArgs .length == 2 && typeArgs [1 ] != null ) {
295+ v1alpha1FunctionSpecOutput .setTypeClassName (typeArgs [1 ].getName ());
296+ }
297+ }
250298 } else if (StringUtils .isNotEmpty (functionConfig .getPy ())) {
251299 V1alpha1FunctionSpecPython v1alpha1FunctionSpecPython = new V1alpha1FunctionSpecPython ();
252- Path path = Paths .get (functionConfig .getPy ());
253- v1alpha1FunctionSpecPython .setPy (path .getFileName ().toString ());
254- v1alpha1FunctionSpecPython .setPyLocation (location );
300+ v1alpha1FunctionSpecPython .setPy (fileName );
301+ if (isPkgUrlProvided ) {
302+ v1alpha1FunctionSpecPython .setPyLocation (functionPkgUrl );
303+ }
255304 v1alpha1FunctionSpec .setPython (v1alpha1FunctionSpecPython );
256305 } else if (StringUtils .isNotEmpty (functionConfig .getGo ())) {
257306 V1alpha1FunctionSpecGolang v1alpha1FunctionSpecGolang = new V1alpha1FunctionSpecGolang ();
258- Path path = Paths .get (functionConfig .getGo ());
259- v1alpha1FunctionSpecGolang .setGo (path .getFileName ().toString ());
260- v1alpha1FunctionSpecGolang .setGoLocation (location );
307+ v1alpha1FunctionSpecGolang .setGo (fileName );
308+ if (isPkgUrlProvided ) {
309+ v1alpha1FunctionSpecGolang .setGoLocation (functionPkgUrl );
310+ }
261311 v1alpha1FunctionSpec .setGolang (v1alpha1FunctionSpecGolang );
262312 }
263313
@@ -422,12 +472,21 @@ public static FunctionConfig createFunctionConfigFromV1alpha1Function(String ten
422472 if (v1alpha1FunctionSpec .getJava () != null ) {
423473 functionConfig .setRuntime (FunctionConfig .Runtime .JAVA );
424474 functionConfig .setJar (v1alpha1FunctionSpec .getJava ().getJar ());
475+ if (Strings .isNotEmpty (v1alpha1FunctionSpec .getJava ().getJarLocation ())) {
476+ functionConfig .setJar (v1alpha1FunctionSpec .getJava ().getJarLocation ());
477+ }
425478 } else if (v1alpha1FunctionSpec .getPython () != null ) {
426479 functionConfig .setRuntime (FunctionConfig .Runtime .PYTHON );
427480 functionConfig .setPy (v1alpha1FunctionSpec .getPython ().getPy ());
481+ if (Strings .isNotEmpty (v1alpha1FunctionSpec .getPython ().getPyLocation ())) {
482+ functionConfig .setJar (v1alpha1FunctionSpec .getPython ().getPyLocation ());
483+ }
428484 } else if (v1alpha1FunctionSpec .getGolang () != null ) {
429485 functionConfig .setRuntime (FunctionConfig .Runtime .GO );
430486 functionConfig .setGo (v1alpha1FunctionSpec .getGolang ().getGo ());
487+ if (Strings .isNotEmpty (v1alpha1FunctionSpec .getGolang ().getGoLocation ())) {
488+ functionConfig .setJar (v1alpha1FunctionSpec .getGolang ().getGoLocation ());
489+ }
431490 }
432491 if (v1alpha1FunctionSpec .getMaxMessageRetry () != null ) {
433492 functionConfig .setMaxMessageRetries (v1alpha1FunctionSpec .getMaxMessageRetry ());
@@ -497,4 +556,35 @@ public static void convertFunctionStatusToInstanceStatusData(InstanceCommunicati
497556 functionInstanceStatusData .setLastInvocationTime (functionStatus .getLastInvocationTime ());
498557 }
499558
559+ private static File downloadPackageFile (MeshWorkerService worker , String packageName ) throws IOException , PulsarAdminException {
560+ Path tempDirectory ;
561+ if (worker .getWorkerConfig ().getDownloadDirectory () != null ) {
562+ tempDirectory = Paths .get (worker .getWorkerConfig ().getDownloadDirectory ());
563+ } else {
564+ // use the Nar extraction directory as a temporary directory for downloaded files
565+ tempDirectory = Paths .get (worker .getWorkerConfig ().getNarExtractionDirectory ());
566+ }
567+ File file = Files .createTempFile (tempDirectory , "function" , ".tmp" ).toFile ();
568+ worker .getBrokerAdmin ().packages ().download (packageName , file .toString ());
569+ return file ;
570+ }
571+
572+ private static Class <?>[] extractTypeArgs (final FunctionConfig functionConfig ,
573+ final File componentPackageFile ) {
574+ Class <?>[] typeArgs = null ;
575+ if (componentPackageFile == null ) {
576+ return null ;
577+ }
578+ ClassLoader clsLoader = FunctionConfigUtils .validate (functionConfig , componentPackageFile );
579+ if (functionConfig .getRuntime () == FunctionConfig .Runtime .JAVA && clsLoader != null ) {
580+ try {
581+ typeArgs = FunctionCommon .getFunctionTypes (functionConfig , clsLoader );
582+ } catch (ClassNotFoundException | NoClassDefFoundError e ) {
583+ throw new IllegalArgumentException (
584+ String .format ("Function class %s must be in class path" , functionConfig .getClassName ()), e );
585+ }
586+ }
587+ return typeArgs ;
588+ }
589+
500590}
0 commit comments