From ded513d86b3c6d7106d6ff2f70bfbc4e7f85097e Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Fri, 20 Aug 2021 11:51:08 +0800 Subject: [PATCH 1/8] add packages url support in mesh worker service --- mesh-worker-service/pom.xml | 2 +- .../compute/rest/api/FunctionsImpl.java | 16 ++- .../compute/util/FunctionsUtil.java | 118 +++++++++++++++--- .../compute/rest/api/FunctionsImplTest.java | 3 +- .../compute/util/FunctionsUtilTest.java | 35 +++++- 5 files changed, 152 insertions(+), 22 deletions(-) diff --git a/mesh-worker-service/pom.xml b/mesh-worker-service/pom.xml index d7c51ce6b..c52d7f0d0 100644 --- a/mesh-worker-service/pom.xml +++ b/mesh-worker-service/pom.xml @@ -29,7 +29,7 @@ v0.1.7-rc1 - 2.8.0.7 + 2.8.0.13 1.18.16 2.14.0 12.0.1 diff --git a/mesh-worker-service/src/main/java/io/functionmesh/compute/rest/api/FunctionsImpl.java b/mesh-worker-service/src/main/java/io/functionmesh/compute/rest/api/FunctionsImpl.java index d40509cbc..d3426e79d 100644 --- a/mesh-worker-service/src/main/java/io/functionmesh/compute/rest/api/FunctionsImpl.java +++ b/mesh-worker-service/src/main/java/io/functionmesh/compute/rest/api/FunctionsImpl.java @@ -42,6 +42,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.pulsar.broker.authentication.AuthenticationDataHttps; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; +import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.common.functions.FunctionConfig; import org.apache.pulsar.common.functions.UpdateOptionsImpl; import org.apache.pulsar.common.policies.data.FunctionStatus; @@ -50,12 +51,18 @@ import org.apache.pulsar.functions.proto.InstanceCommunication; import org.apache.pulsar.functions.proto.InstanceControlGrpc; import org.apache.pulsar.functions.utils.ComponentTypeUtils; +import org.apache.pulsar.functions.worker.PulsarWorkerService; import org.apache.pulsar.functions.worker.service.api.Functions; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import javax.ws.rs.core.Response; +import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -142,8 +149,8 @@ public void registerFunction(final String tenant, functionName, functionPkgUrl, functionConfig, - worker().getWorkerConfig().getFunctionsWorkerServiceCustomConfigs(), - cluster + cluster, + worker() ); // override namespace by configuration file v1alpha1Function.getMetadata().setNamespace(KubernetesUtils.getNamespace(worker().getFactoryConfig())); @@ -211,8 +218,8 @@ public void updateFunction(final String tenant, functionName, functionPkgUrl, functionConfig, - worker().getWorkerConfig().getFunctionsWorkerServiceCustomConfigs(), - cluster + cluster, + worker() ); Call getCall = worker().getCustomObjectsApi().getNamespacedCustomObjectCall( group, @@ -611,4 +618,5 @@ private void upsertFunction(final String tenant, } } } + } diff --git a/mesh-worker-service/src/main/java/io/functionmesh/compute/util/FunctionsUtil.java b/mesh-worker-service/src/main/java/io/functionmesh/compute/util/FunctionsUtil.java index c6e35bffe..b7fce1728 100644 --- a/mesh-worker-service/src/main/java/io/functionmesh/compute/util/FunctionsUtil.java +++ b/mesh-worker-service/src/main/java/io/functionmesh/compute/util/FunctionsUtil.java @@ -19,6 +19,7 @@ package io.functionmesh.compute.util; import com.google.gson.Gson; +import io.functionmesh.compute.MeshWorkerService; import io.functionmesh.compute.functions.models.V1alpha1Function; import io.functionmesh.compute.functions.models.V1alpha1FunctionSpec; import io.functionmesh.compute.functions.models.V1alpha1FunctionSpecGolang; @@ -36,18 +37,26 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.util.Strings; +import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.common.functions.ConsumerConfig; import org.apache.pulsar.common.functions.FunctionConfig; import org.apache.pulsar.common.functions.ProducerConfig; import org.apache.pulsar.common.functions.Resources; +import org.apache.pulsar.common.functions.Utils; import org.apache.pulsar.common.policies.data.ExceptionInformation; import org.apache.pulsar.common.policies.data.FunctionStatus; import org.apache.pulsar.common.util.RestException; import org.apache.pulsar.functions.proto.Function; import org.apache.pulsar.functions.proto.InstanceCommunication; +import org.apache.pulsar.functions.utils.ComponentTypeUtils; +import org.apache.pulsar.functions.utils.FunctionCommon; import org.apache.pulsar.functions.utils.FunctionConfigUtils; +import org.apache.pulsar.functions.worker.PulsarWorkerService; import javax.ws.rs.core.Response; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -66,7 +75,8 @@ public class FunctionsUtil { public static V1alpha1Function createV1alpha1FunctionFromFunctionConfig(String kind, String group, String version , String functionName, String functionPkgUrl, FunctionConfig functionConfig - , Map customConfigs, String cluster) { + , String cluster, MeshWorkerService worker) { + Map customConfigs = worker.getWorkerConfig().getFunctionsWorkerServiceCustomConfigs(); CustomRuntimeOptions customRuntimeOptions = CommonUtil.getCustomRuntimeOptions(functionConfig.getCustomRuntimeOptions()); String clusterName = CommonUtil.getClusterName(cluster, customRuntimeOptions); @@ -236,28 +246,69 @@ public static V1alpha1Function createV1alpha1FunctionFromFunctionConfig(String k // v1alpha1FunctionSpecPulsar.setAuthConfig(CommonUtil.getPulsarClusterAuthConfigMapName(clusterName)); v1alpha1FunctionSpec.setPulsar(v1alpha1FunctionSpecPulsar); - String location = String.format("%s/%s/%s", functionConfig.getTenant(), functionConfig.getNamespace(), - functionName); - if (StringUtils.isNotEmpty(functionPkgUrl)) { - location = functionPkgUrl; + // TODO: dynamic file name to function CRD + String fileName = "/pulsar/function-executable"; + boolean isPkgUrlProvided = StringUtils.isNotEmpty(functionPkgUrl); + File componentPackageFile = null; + try { + if (isPkgUrlProvided) { + if (Utils.hasPackageTypePrefix(functionPkgUrl)) { + componentPackageFile = downloadPackageFile(worker, functionPkgUrl); + } else { + log.warn("get unsupported function package url {}", functionPkgUrl); + throw new IllegalArgumentException("Function Package url is not valid. supported url (function/sink/source)"); + } + } else { + // TODO: support upload JAR to bk + throw new IllegalArgumentException("uploading package to mesh worker service is not supported yet."); + } + } catch (Exception e) { + log.error("Invalid register function request {}: {}", functionName, e); + throw new RestException(Response.Status.BAD_REQUEST, e.getMessage()); + } + Class[] typeArgs = null; + if (componentPackageFile != null) { + typeArgs = extractTypeArgs(functionConfig, componentPackageFile); } if (StringUtils.isNotEmpty(functionConfig.getJar())) { V1alpha1FunctionSpecJava v1alpha1FunctionSpecJava = new V1alpha1FunctionSpecJava(); - Path path = Paths.get(functionConfig.getJar()); - v1alpha1FunctionSpecJava.setJar(path.getFileName().toString()); - v1alpha1FunctionSpecJava.setJarLocation(location); + v1alpha1FunctionSpecJava.setJar(fileName); + if (isPkgUrlProvided) { + v1alpha1FunctionSpecJava.setJarLocation(functionPkgUrl); + } + String extraDependenciesDir = ""; + if (StringUtils.isNotEmpty(worker.getFactoryConfig().getExtraFunctionDependenciesDir())) { + if (Paths.get(worker.getFactoryConfig().getExtraFunctionDependenciesDir()).isAbsolute()) { + extraDependenciesDir = worker.getFactoryConfig().getExtraFunctionDependenciesDir(); + } else { + extraDependenciesDir = "/pulsar/" + worker.getFactoryConfig().getExtraFunctionDependenciesDir(); + } + } else { + extraDependenciesDir = "/pulsar/instances/deps"; + } + v1alpha1FunctionSpecJava.setExtraDependenciesDir(extraDependenciesDir); v1alpha1FunctionSpec.setJava(v1alpha1FunctionSpecJava); + if (typeArgs != null) { + if (typeArgs.length == 2 && typeArgs[0] != null) { + v1alpha1FunctionSpecInput.setTypeClassName(typeArgs[0].getName()); + } + if (typeArgs.length == 2 && typeArgs[1] != null) { + v1alpha1FunctionSpecOutput.setTypeClassName(typeArgs[1].getName()); + } + } } else if (StringUtils.isNotEmpty(functionConfig.getPy())) { V1alpha1FunctionSpecPython v1alpha1FunctionSpecPython = new V1alpha1FunctionSpecPython(); - Path path = Paths.get(functionConfig.getPy()); - v1alpha1FunctionSpecPython.setPy(path.getFileName().toString()); - v1alpha1FunctionSpecPython.setPyLocation(location); + v1alpha1FunctionSpecPython.setPy(fileName); + if (isPkgUrlProvided) { + v1alpha1FunctionSpecPython.setPyLocation(functionPkgUrl); + } v1alpha1FunctionSpec.setPython(v1alpha1FunctionSpecPython); } else if (StringUtils.isNotEmpty(functionConfig.getGo())) { V1alpha1FunctionSpecGolang v1alpha1FunctionSpecGolang = new V1alpha1FunctionSpecGolang(); - Path path = Paths.get(functionConfig.getGo()); - v1alpha1FunctionSpecGolang.setGo(path.getFileName().toString()); - v1alpha1FunctionSpecGolang.setGoLocation(location); + v1alpha1FunctionSpecGolang.setGo(fileName); + if (isPkgUrlProvided) { + v1alpha1FunctionSpecGolang.setGoLocation(functionPkgUrl); + } v1alpha1FunctionSpec.setGolang(v1alpha1FunctionSpecGolang); } @@ -422,12 +473,21 @@ public static FunctionConfig createFunctionConfigFromV1alpha1Function(String ten if (v1alpha1FunctionSpec.getJava() != null) { functionConfig.setRuntime(FunctionConfig.Runtime.JAVA); functionConfig.setJar(v1alpha1FunctionSpec.getJava().getJar()); + if (Strings.isNotEmpty(v1alpha1FunctionSpec.getJava().getJarLocation())) { + functionConfig.setJar(v1alpha1FunctionSpec.getJava().getJarLocation()); + } } else if (v1alpha1FunctionSpec.getPython() != null) { functionConfig.setRuntime(FunctionConfig.Runtime.PYTHON); functionConfig.setPy(v1alpha1FunctionSpec.getPython().getPy()); + if (Strings.isNotEmpty(v1alpha1FunctionSpec.getPython().getPyLocation())) { + functionConfig.setJar(v1alpha1FunctionSpec.getPython().getPyLocation()); + } } else if (v1alpha1FunctionSpec.getGolang() != null) { functionConfig.setRuntime(FunctionConfig.Runtime.GO); functionConfig.setGo(v1alpha1FunctionSpec.getGolang().getGo()); + if (Strings.isNotEmpty(v1alpha1FunctionSpec.getGolang().getGoLocation())) { + functionConfig.setJar(v1alpha1FunctionSpec.getGolang().getGoLocation()); + } } if (v1alpha1FunctionSpec.getMaxMessageRetry() != null) { functionConfig.setMaxMessageRetries(v1alpha1FunctionSpec.getMaxMessageRetry()); @@ -497,4 +557,34 @@ public static void convertFunctionStatusToInstanceStatusData(InstanceCommunicati functionInstanceStatusData.setLastInvocationTime(functionStatus.getLastInvocationTime()); } + static File downloadPackageFile(MeshWorkerService worker, String packageName) throws IOException, PulsarAdminException { + Path tempDirectory; + if (worker.getWorkerConfig().getDownloadDirectory() != null) { + tempDirectory = Paths.get(worker.getWorkerConfig().getDownloadDirectory()); + } else { + // use the Nar extraction directory as a temporary directory for downloaded files + tempDirectory = Paths.get(worker.getWorkerConfig().getNarExtractionDirectory()); + } + File file = Files.createTempFile(tempDirectory, "function", ".tmp").toFile(); + worker.getBrokerAdmin().packages().download(packageName, file.toString()); + return file; + } + + private static Class[] extractTypeArgs(final FunctionConfig functionConfig, + final File componentPackageFile) { + ClassLoader clsLoader = FunctionConfigUtils.validate(functionConfig, componentPackageFile); + Class[] typeArgs = null; + if (functionConfig.getRuntime() == FunctionConfig.Runtime.JAVA) { + if (clsLoader != null) { + try { + typeArgs = FunctionCommon.getFunctionTypes(functionConfig, clsLoader); + } catch (ClassNotFoundException | NoClassDefFoundError e) { + throw new IllegalArgumentException( + String.format("Function class %s must be in class path", functionConfig.getClassName()), e); + } + } + } + return typeArgs; + } + } diff --git a/mesh-worker-service/src/test/java/io/functionmesh/compute/rest/api/FunctionsImplTest.java b/mesh-worker-service/src/test/java/io/functionmesh/compute/rest/api/FunctionsImplTest.java index 78f158445..299d1cf5a 100644 --- a/mesh-worker-service/src/test/java/io/functionmesh/compute/rest/api/FunctionsImplTest.java +++ b/mesh-worker-service/src/test/java/io/functionmesh/compute/rest/api/FunctionsImplTest.java @@ -319,6 +319,7 @@ public void registerFunctionTest() throws ApiException, IOException, PulsarAdmin PowerMockito.when(meshWorkerService.getWorkerConfig()).thenReturn(workerConfig); PowerMockito.when(workerConfig.isAuthorizationEnabled()).thenReturn(false); PowerMockito.when(workerConfig.isAuthenticationEnabled()).thenReturn(false); + PowerMockito.when(workerConfig.getFunctionsWorkerServiceCustomConfigs()).thenReturn(Collections.emptyMap()); PulsarAdmin pulsarAdmin = PowerMockito.mock(PulsarAdmin.class); PowerMockito.when(meshWorkerService.getBrokerAdmin()).thenReturn(pulsarAdmin); Tenants tenants = PowerMockito.mock(Tenants.class); @@ -341,7 +342,7 @@ public void registerFunctionTest() throws ApiException, IOException, PulsarAdmin PowerMockito.when(tenants.getTenantInfo(tenant)).thenReturn(null); V1alpha1Function v1alpha1Function = FunctionsUtil.createV1alpha1FunctionFromFunctionConfig(kind, group, - version, functionName, null, functionConfig, Collections.emptyMap(), null); + version, functionName, null, functionConfig, null, meshWorkerService); String clusterName = "test-pulsar"; Map customLabels = Maps.newHashMap(); diff --git a/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java b/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java index b56213e98..9138da3d8 100644 --- a/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java +++ b/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java @@ -18,13 +18,20 @@ */ package io.functionmesh.compute.util; +import io.functionmesh.compute.MeshWorkerService; import io.functionmesh.compute.functions.models.V1alpha1Function; import io.functionmesh.compute.functions.models.V1alpha1FunctionSpec; import io.functionmesh.compute.testdata.Generate; import java.util.Collections; +import java.util.function.Supplier; + +import io.kubernetes.client.openapi.apis.CustomObjectsApi; +import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.common.functions.FunctionConfig; +import org.apache.pulsar.functions.worker.WorkerConfig; import org.junit.Assert; import org.junit.Test; +import org.powermock.api.mockito.PowerMockito; public class FunctionsUtilTest { @Test @@ -43,10 +50,22 @@ public void testCreateV1alpha1FunctionFromFunctionConfig() { String clusterName = "test-pulsar"; String jar = "word-count.jar"; + MeshWorkerService meshWorkerService = PowerMockito.mock(MeshWorkerService.class); + Supplier meshWorkerServiceSupplier = () -> meshWorkerService; + CustomObjectsApi customObjectsApi = PowerMockito.mock(CustomObjectsApi.class); + PowerMockito.when(meshWorkerService.getCustomObjectsApi()).thenReturn(customObjectsApi); + WorkerConfig workerConfig = PowerMockito.mock(WorkerConfig.class); + PowerMockito.when(meshWorkerService.getWorkerConfig()).thenReturn(workerConfig); + PowerMockito.when(workerConfig.isAuthorizationEnabled()).thenReturn(false); + PowerMockito.when(workerConfig.isAuthenticationEnabled()).thenReturn(false); + PowerMockito.when(workerConfig.getFunctionsWorkerServiceCustomConfigs()).thenReturn(Collections.emptyMap()); + PulsarAdmin pulsarAdmin = PowerMockito.mock(PulsarAdmin.class); + PowerMockito.when(meshWorkerService.getBrokerAdmin()).thenReturn(pulsarAdmin); + FunctionConfig functionConfig = Generate.CreateJavaFunctionConfig(tenant, namespace, functionName); V1alpha1Function v1alpha1Function = FunctionsUtil.createV1alpha1FunctionFromFunctionConfig(kind, group, version, - functionName, null, functionConfig, Collections.emptyMap(), null); + functionName, null, functionConfig, null, meshWorkerService); Assert.assertEquals(v1alpha1Function.getKind(), kind); @@ -73,10 +92,22 @@ public void testCreateFunctionConfigFromV1alpha1Function() { String version = "v1alpha1"; String kind = "Function"; + MeshWorkerService meshWorkerService = PowerMockito.mock(MeshWorkerService.class); + Supplier meshWorkerServiceSupplier = () -> meshWorkerService; + CustomObjectsApi customObjectsApi = PowerMockito.mock(CustomObjectsApi.class); + PowerMockito.when(meshWorkerService.getCustomObjectsApi()).thenReturn(customObjectsApi); + WorkerConfig workerConfig = PowerMockito.mock(WorkerConfig.class); + PowerMockito.when(meshWorkerService.getWorkerConfig()).thenReturn(workerConfig); + PowerMockito.when(workerConfig.isAuthorizationEnabled()).thenReturn(false); + PowerMockito.when(workerConfig.isAuthenticationEnabled()).thenReturn(false); + PowerMockito.when(workerConfig.getFunctionsWorkerServiceCustomConfigs()).thenReturn(Collections.emptyMap()); + PulsarAdmin pulsarAdmin = PowerMockito.mock(PulsarAdmin.class); + PowerMockito.when(meshWorkerService.getBrokerAdmin()).thenReturn(pulsarAdmin); + FunctionConfig functionConfig = Generate.CreateJavaFunctionConfig(tenant, namespace, functionName); V1alpha1Function v1alpha1Function = FunctionsUtil.createV1alpha1FunctionFromFunctionConfig(kind, group, version, - functionName, null, functionConfig, Collections.emptyMap(), null); + functionName, null, functionConfig, null, meshWorkerService); FunctionConfig newFunctionConfig = FunctionsUtil.createFunctionConfigFromV1alpha1Function(tenant, namespace, functionName, v1alpha1Function); From 2a66b2e92607960ea629a16b7f3e3615bfee2623 Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Mon, 23 Aug 2021 14:04:24 +0800 Subject: [PATCH 2/8] fix CI --- .../compute/rest/api/FunctionsImpl.java | 1 + .../compute/util/FunctionsUtil.java | 8 +++- .../compute/rest/api/FunctionsImplTest.java | 24 +++++++++--- .../compute/testdata/Generate.java | 25 ++++++++++++ .../compute/util/FunctionsUtilTest.java | 39 ++++++++++++++++--- 5 files changed, 84 insertions(+), 13 deletions(-) diff --git a/mesh-worker-service/src/main/java/io/functionmesh/compute/rest/api/FunctionsImpl.java b/mesh-worker-service/src/main/java/io/functionmesh/compute/rest/api/FunctionsImpl.java index d3426e79d..190364bd8 100644 --- a/mesh-worker-service/src/main/java/io/functionmesh/compute/rest/api/FunctionsImpl.java +++ b/mesh-worker-service/src/main/java/io/functionmesh/compute/rest/api/FunctionsImpl.java @@ -253,6 +253,7 @@ public void updateFunction(final String tenant, executeCall(replaceCall, V1alpha1Function.class); } catch (Exception e) { log.error("update {}/{}/{} function failed, error message: {}", tenant, namespace, functionName, e); + e.printStackTrace(); throw new RestException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()); } } diff --git a/mesh-worker-service/src/main/java/io/functionmesh/compute/util/FunctionsUtil.java b/mesh-worker-service/src/main/java/io/functionmesh/compute/util/FunctionsUtil.java index b7fce1728..42e363736 100644 --- a/mesh-worker-service/src/main/java/io/functionmesh/compute/util/FunctionsUtil.java +++ b/mesh-worker-service/src/main/java/io/functionmesh/compute/util/FunctionsUtil.java @@ -264,6 +264,7 @@ public static V1alpha1Function createV1alpha1FunctionFromFunctionConfig(String k } } catch (Exception e) { log.error("Invalid register function request {}: {}", functionName, e); + e.printStackTrace(); throw new RestException(Response.Status.BAD_REQUEST, e.getMessage()); } Class[] typeArgs = null; @@ -557,7 +558,7 @@ public static void convertFunctionStatusToInstanceStatusData(InstanceCommunicati functionInstanceStatusData.setLastInvocationTime(functionStatus.getLastInvocationTime()); } - static File downloadPackageFile(MeshWorkerService worker, String packageName) throws IOException, PulsarAdminException { + private static File downloadPackageFile(MeshWorkerService worker, String packageName) throws IOException, PulsarAdminException { Path tempDirectory; if (worker.getWorkerConfig().getDownloadDirectory() != null) { tempDirectory = Paths.get(worker.getWorkerConfig().getDownloadDirectory()); @@ -572,8 +573,11 @@ static File downloadPackageFile(MeshWorkerService worker, String packageName) th private static Class[] extractTypeArgs(final FunctionConfig functionConfig, final File componentPackageFile) { - ClassLoader clsLoader = FunctionConfigUtils.validate(functionConfig, componentPackageFile); Class[] typeArgs = null; + if (componentPackageFile == null) { + return null; + } + ClassLoader clsLoader = FunctionConfigUtils.validate(functionConfig, componentPackageFile); if (functionConfig.getRuntime() == FunctionConfig.Runtime.JAVA) { if (clsLoader != null) { try { diff --git a/mesh-worker-service/src/test/java/io/functionmesh/compute/rest/api/FunctionsImplTest.java b/mesh-worker-service/src/test/java/io/functionmesh/compute/rest/api/FunctionsImplTest.java index 299d1cf5a..13fd646fb 100644 --- a/mesh-worker-service/src/test/java/io/functionmesh/compute/rest/api/FunctionsImplTest.java +++ b/mesh-worker-service/src/test/java/io/functionmesh/compute/rest/api/FunctionsImplTest.java @@ -44,6 +44,7 @@ import okhttp3.ResponseBody; import okhttp3.internal.http.RealResponseBody; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.pulsar.client.admin.Packages; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.client.admin.Tenants; @@ -51,6 +52,7 @@ import org.apache.pulsar.common.policies.data.FunctionStatus; import org.apache.pulsar.functions.proto.InstanceCommunication; import org.apache.pulsar.functions.proto.InstanceControlGrpc; +import org.apache.pulsar.functions.runtime.kubernetes.KubernetesRuntimeFactoryConfig; import org.apache.pulsar.functions.worker.WorkerConfig; import org.junit.Assert; import org.junit.Test; @@ -60,6 +62,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -78,6 +81,7 @@ Response.class, RealResponseBody.class, CommonUtil.class, + FunctionsUtil.class, InstanceControlGrpc.InstanceControlFutureStub.class}) @PowerMockIgnore({"javax.management.*"}) public class FunctionsImplTest { @@ -316,7 +320,10 @@ public void registerFunctionTest() throws ApiException, IOException, PulsarAdmin CustomObjectsApi customObjectsApi = PowerMockito.mock(CustomObjectsApi.class); PowerMockito.when(meshWorkerService.getCustomObjectsApi()).thenReturn(customObjectsApi); WorkerConfig workerConfig = PowerMockito.mock(WorkerConfig.class); + KubernetesRuntimeFactoryConfig factoryConfig = PowerMockito.mock(KubernetesRuntimeFactoryConfig.class); PowerMockito.when(meshWorkerService.getWorkerConfig()).thenReturn(workerConfig); + PowerMockito.when(meshWorkerService.getFactoryConfig()).thenReturn(factoryConfig); + PowerMockito.when(factoryConfig.getExtraFunctionDependenciesDir()).thenReturn(""); PowerMockito.when(workerConfig.isAuthorizationEnabled()).thenReturn(false); PowerMockito.when(workerConfig.isAuthenticationEnabled()).thenReturn(false); PowerMockito.when(workerConfig.getFunctionsWorkerServiceCustomConfigs()).thenReturn(Collections.emptyMap()); @@ -328,6 +335,7 @@ public void registerFunctionTest() throws ApiException, IOException, PulsarAdmin Response response = PowerMockito.mock(Response.class); ResponseBody responseBody = PowerMockito.mock(RealResponseBody.class); ApiClient apiClient = PowerMockito.mock(ApiClient.class); + PowerMockito.stub(PowerMockito.method(FunctionsUtil.class, "downloadPackageFile")).toReturn(null); String tenant = "public"; String namespace = "default"; @@ -337,12 +345,12 @@ public void registerFunctionTest() throws ApiException, IOException, PulsarAdmin String version = "v1alpha1"; String kind = "Function"; - FunctionConfig functionConfig = Generate.CreateJavaFunctionConfig(tenant, namespace, functionName); + FunctionConfig functionConfig = Generate.CreateJavaFunctionWithPackageURLConfig(tenant, namespace, functionName); PowerMockito.when(tenants.getTenantInfo(tenant)).thenReturn(null); V1alpha1Function v1alpha1Function = FunctionsUtil.createV1alpha1FunctionFromFunctionConfig(kind, group, - version, functionName, null, functionConfig, null, meshWorkerService); + version, functionName, functionConfig.getJar(), functionConfig, null, meshWorkerService); String clusterName = "test-pulsar"; Map customLabels = Maps.newHashMap(); @@ -381,7 +389,7 @@ public void registerFunctionTest() throws ApiException, IOException, PulsarAdmin functionName, null, null, - null, + functionConfig.getJar(), functionConfig, null, null); @@ -508,6 +516,9 @@ public void updateFunctionTest() throws ApiException, IOException { PowerMockito.when(meshWorkerService.getWorkerConfig()).thenReturn(workerConfig); PowerMockito.when(workerConfig.isAuthorizationEnabled()).thenReturn(false); PowerMockito.when(workerConfig.isAuthenticationEnabled()).thenReturn(false); + KubernetesRuntimeFactoryConfig factoryConfig = PowerMockito.mock(KubernetesRuntimeFactoryConfig.class); + PowerMockito.when(meshWorkerService.getFactoryConfig()).thenReturn(factoryConfig); + PowerMockito.when(factoryConfig.getExtraFunctionDependenciesDir()).thenReturn(""); Call getCall = PowerMockito.mock(Call.class); Response getResponse = PowerMockito.mock(Response.class); @@ -541,7 +552,9 @@ public void updateFunctionTest() throws ApiException, IOException { null )).thenReturn(getCall); - FunctionConfig functionConfig = Generate.CreateJavaFunctionConfig(tenant, namespace, functionName); + PowerMockito.stub(PowerMockito.method(FunctionsUtil.class, "downloadPackageFile")).toReturn(null); + + FunctionConfig functionConfig = Generate.CreateJavaFunctionWithPackageURLConfig(tenant, namespace, functionName); PowerMockito.when(meshWorkerService.getCustomObjectsApi() .replaceNamespacedCustomObjectCall( @@ -565,12 +578,13 @@ public void updateFunctionTest() throws ApiException, IOException { functionName, null, null, - null, + functionConfig.getJar(), functionConfig, null, null, null); } catch (Exception exception) { + exception.printStackTrace(); Assert.fail("Expected no exception to be thrown but got exception: " + exception); } } diff --git a/mesh-worker-service/src/test/java/io/functionmesh/compute/testdata/Generate.java b/mesh-worker-service/src/test/java/io/functionmesh/compute/testdata/Generate.java index 7540a3130..d199e6fc1 100644 --- a/mesh-worker-service/src/test/java/io/functionmesh/compute/testdata/Generate.java +++ b/mesh-worker-service/src/test/java/io/functionmesh/compute/testdata/Generate.java @@ -59,6 +59,31 @@ public static FunctionConfig CreateJavaFunctionConfig(String tenant, String name return functionConfig; } + public static FunctionConfig CreateJavaFunctionWithPackageURLConfig(String tenant, String namespace, String functionName) { + FunctionConfig functionConfig = new FunctionConfig(); + functionConfig.setName(functionName); + functionConfig.setTenant(tenant); + functionConfig.setNamespace(namespace); + functionConfig.setClassName("org.example.functions.WordCountFunction"); + functionConfig.setInputs(Collections.singletonList("persistent://public/default/sentences")); + functionConfig.setParallelism(1); + functionConfig.setCleanupSubscription(true); + functionConfig.setOutput("persistent://public/default/count"); + Resources resources = new Resources(); + resources.setCpu(1.0); + resources.setRam(102400L); + functionConfig.setResources(resources); + CustomRuntimeOptions customRuntimeOptions = new CustomRuntimeOptions(); + customRuntimeOptions.setClusterName(TEST_CLUSTER_NAME); + customRuntimeOptions.setInputTypeClassName("java.lang.String"); + customRuntimeOptions.setOutputTypeClassName("java.lang.String"); + String customRuntimeOptionsJSON = new Gson().toJson(customRuntimeOptions, CustomRuntimeOptions.class); + functionConfig.setCustomRuntimeOptions(customRuntimeOptionsJSON); + functionConfig.setJar(String.format("function://public/default/%s@1.0", functionName)); + functionConfig.setAutoAck(true); + return functionConfig; + } + public static SinkConfig CreateSinkConfig(String tenant, String namespace, String functionName) { SinkConfig sinkConfig = new SinkConfig(); sinkConfig.setName(functionName); diff --git a/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java b/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java index 9138da3d8..8288f6abc 100644 --- a/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java +++ b/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java @@ -22,17 +22,35 @@ import io.functionmesh.compute.functions.models.V1alpha1Function; import io.functionmesh.compute.functions.models.V1alpha1FunctionSpec; import io.functionmesh.compute.testdata.Generate; + +import java.io.File; import java.util.Collections; import java.util.function.Supplier; import io.kubernetes.client.openapi.apis.CustomObjectsApi; +import okhttp3.Response; +import okhttp3.internal.http.RealResponseBody; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.common.functions.FunctionConfig; +import org.apache.pulsar.functions.proto.InstanceControlGrpc; +import org.apache.pulsar.functions.runtime.kubernetes.KubernetesRuntimeFactoryConfig; import org.apache.pulsar.functions.worker.WorkerConfig; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; - +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({ + Response.class, + RealResponseBody.class, + CommonUtil.class, + FunctionsUtil.class, + InstanceControlGrpc.InstanceControlFutureStub.class}) +@PowerMockIgnore({"javax.management.*"}) public class FunctionsUtilTest { @Test public void testCreateV1alpha1FunctionFromFunctionConfig() { @@ -48,24 +66,29 @@ public void testCreateV1alpha1FunctionFromFunctionConfig() { String input = "persistent://public/default/sentences"; String output = "persistent://public/default/count"; String clusterName = "test-pulsar"; - String jar = "word-count.jar"; + String jar = "/pulsar/function-executable"; MeshWorkerService meshWorkerService = PowerMockito.mock(MeshWorkerService.class); Supplier meshWorkerServiceSupplier = () -> meshWorkerService; CustomObjectsApi customObjectsApi = PowerMockito.mock(CustomObjectsApi.class); PowerMockito.when(meshWorkerService.getCustomObjectsApi()).thenReturn(customObjectsApi); WorkerConfig workerConfig = PowerMockito.mock(WorkerConfig.class); + KubernetesRuntimeFactoryConfig factoryConfig = PowerMockito.mock(KubernetesRuntimeFactoryConfig.class); PowerMockito.when(meshWorkerService.getWorkerConfig()).thenReturn(workerConfig); + PowerMockito.when(meshWorkerService.getFactoryConfig()).thenReturn(factoryConfig); + PowerMockito.when(factoryConfig.getExtraFunctionDependenciesDir()).thenReturn(""); PowerMockito.when(workerConfig.isAuthorizationEnabled()).thenReturn(false); PowerMockito.when(workerConfig.isAuthenticationEnabled()).thenReturn(false); PowerMockito.when(workerConfig.getFunctionsWorkerServiceCustomConfigs()).thenReturn(Collections.emptyMap()); PulsarAdmin pulsarAdmin = PowerMockito.mock(PulsarAdmin.class); PowerMockito.when(meshWorkerService.getBrokerAdmin()).thenReturn(pulsarAdmin); + PowerMockito.stub(PowerMockito.method(FunctionsUtil.class, "downloadPackageFile")).toReturn(null); + - FunctionConfig functionConfig = Generate.CreateJavaFunctionConfig(tenant, namespace, functionName); + FunctionConfig functionConfig = Generate.CreateJavaFunctionWithPackageURLConfig(tenant, namespace, functionName); V1alpha1Function v1alpha1Function = FunctionsUtil.createV1alpha1FunctionFromFunctionConfig(kind, group, version, - functionName, null, functionConfig, null, meshWorkerService); + functionName, functionConfig.getJar(), functionConfig, null, meshWorkerService); Assert.assertEquals(v1alpha1Function.getKind(), kind); @@ -97,17 +120,21 @@ public void testCreateFunctionConfigFromV1alpha1Function() { CustomObjectsApi customObjectsApi = PowerMockito.mock(CustomObjectsApi.class); PowerMockito.when(meshWorkerService.getCustomObjectsApi()).thenReturn(customObjectsApi); WorkerConfig workerConfig = PowerMockito.mock(WorkerConfig.class); + KubernetesRuntimeFactoryConfig factoryConfig = PowerMockito.mock(KubernetesRuntimeFactoryConfig.class); PowerMockito.when(meshWorkerService.getWorkerConfig()).thenReturn(workerConfig); + PowerMockito.when(meshWorkerService.getFactoryConfig()).thenReturn(factoryConfig); + PowerMockito.when(factoryConfig.getExtraFunctionDependenciesDir()).thenReturn(""); PowerMockito.when(workerConfig.isAuthorizationEnabled()).thenReturn(false); PowerMockito.when(workerConfig.isAuthenticationEnabled()).thenReturn(false); PowerMockito.when(workerConfig.getFunctionsWorkerServiceCustomConfigs()).thenReturn(Collections.emptyMap()); PulsarAdmin pulsarAdmin = PowerMockito.mock(PulsarAdmin.class); PowerMockito.when(meshWorkerService.getBrokerAdmin()).thenReturn(pulsarAdmin); + PowerMockito.stub(PowerMockito.method(FunctionsUtil.class, "downloadPackageFile")).toReturn(null); - FunctionConfig functionConfig = Generate.CreateJavaFunctionConfig(tenant, namespace, functionName); + FunctionConfig functionConfig = Generate.CreateJavaFunctionWithPackageURLConfig(tenant, namespace, functionName); V1alpha1Function v1alpha1Function = FunctionsUtil.createV1alpha1FunctionFromFunctionConfig(kind, group, version, - functionName, null, functionConfig, null, meshWorkerService); + functionName, functionConfig.getJar(), functionConfig, null, meshWorkerService); FunctionConfig newFunctionConfig = FunctionsUtil.createFunctionConfigFromV1alpha1Function(tenant, namespace, functionName, v1alpha1Function); From 24f0e4092aba7dfe4baae13cf074bc243bb83815 Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Mon, 23 Aug 2021 15:02:57 +0800 Subject: [PATCH 3/8] fix CI --- .github/workflows/project.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/project.yml b/.github/workflows/project.yml index 917b4eac3..b0c5cfa20 100644 --- a/.github/workflows/project.yml +++ b/.github/workflows/project.yml @@ -26,10 +26,9 @@ jobs: uses: actions/checkout@v1 - name: InstallKubebuilder - run: | - curl -L https://go.kubebuilder.io/dl/2.3.1/linux/amd64 | tar -xz -C /tmp/ - sudo mv /tmp/kubebuilder_2.3.1_linux_amd64 /usr/local/kubebuilder - export PATH=$PATH:/usr/local/kubebuilder/bin + uses: RyanSiu1995/kubebuilder-action@v1.1 + with: + version: 2.3.1 - name: InstallTool run: | From 2b45e1f6196b4c48320bc77ccf5bc78aa48cd23c Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Mon, 23 Aug 2021 15:14:16 +0800 Subject: [PATCH 4/8] fix github action --- .github/workflows/project.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/project.yml b/.github/workflows/project.yml index b0c5cfa20..7e1a19759 100644 --- a/.github/workflows/project.yml +++ b/.github/workflows/project.yml @@ -26,9 +26,10 @@ jobs: uses: actions/checkout@v1 - name: InstallKubebuilder - uses: RyanSiu1995/kubebuilder-action@v1.1 - with: - version: 2.3.1 + run: | + curl -L https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.3.1/kubebuilder_2.3.1_linux_amd64.tar.gz | tar -xz -C /tmp/ + sudo mv /tmp/kubebuilder_2.3.1_linux_amd64 /usr/local/kubebuilder + export PATH=$PATH:/usr/local/kubebuilder/bin - name: InstallTool run: | From b8f641979f806460b5ae3faa50ff2c6886487ab1 Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Mon, 23 Aug 2021 16:57:17 +0800 Subject: [PATCH 5/8] fix CI --- .ci/helm.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/helm.sh b/.ci/helm.sh index 797973b27..6084974e0 100644 --- a/.ci/helm.sh +++ b/.ci/helm.sh @@ -234,6 +234,7 @@ function ci::verify_mesh_function() { function ci::print_function_log() { FUNCTION_NAME=$1 ${KUBECTL} describe pod -lname=${FUNCTION_NAME} + sleep 120 ${KUBECTL} logs -lname=${FUNCTION_NAME} --all-containers=true } From 18d4bfb72b61371653c7acb2635262e1559137aa Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Mon, 23 Aug 2021 21:05:04 +0800 Subject: [PATCH 6/8] address codacy --- .../compute/rest/api/FunctionsImpl.java | 7 ------- .../functionmesh/compute/util/FunctionsUtil.java | 16 ++++++---------- .../compute/rest/api/FunctionsImplTest.java | 2 -- .../compute/util/FunctionsUtilTest.java | 2 -- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/mesh-worker-service/src/main/java/io/functionmesh/compute/rest/api/FunctionsImpl.java b/mesh-worker-service/src/main/java/io/functionmesh/compute/rest/api/FunctionsImpl.java index 190364bd8..88b64f4a6 100644 --- a/mesh-worker-service/src/main/java/io/functionmesh/compute/rest/api/FunctionsImpl.java +++ b/mesh-worker-service/src/main/java/io/functionmesh/compute/rest/api/FunctionsImpl.java @@ -42,7 +42,6 @@ import org.apache.commons.lang.StringUtils; import org.apache.pulsar.broker.authentication.AuthenticationDataHttps; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; -import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.common.functions.FunctionConfig; import org.apache.pulsar.common.functions.UpdateOptionsImpl; import org.apache.pulsar.common.policies.data.FunctionStatus; @@ -51,18 +50,12 @@ import org.apache.pulsar.functions.proto.InstanceCommunication; import org.apache.pulsar.functions.proto.InstanceControlGrpc; import org.apache.pulsar.functions.utils.ComponentTypeUtils; -import org.apache.pulsar.functions.worker.PulsarWorkerService; import org.apache.pulsar.functions.worker.service.api.Functions; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import javax.ws.rs.core.Response; -import java.io.File; -import java.io.IOException; import java.io.InputStream; import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.HashSet; import java.util.List; import java.util.Map; diff --git a/mesh-worker-service/src/main/java/io/functionmesh/compute/util/FunctionsUtil.java b/mesh-worker-service/src/main/java/io/functionmesh/compute/util/FunctionsUtil.java index 42e363736..08da59231 100644 --- a/mesh-worker-service/src/main/java/io/functionmesh/compute/util/FunctionsUtil.java +++ b/mesh-worker-service/src/main/java/io/functionmesh/compute/util/FunctionsUtil.java @@ -48,10 +48,8 @@ import org.apache.pulsar.common.util.RestException; import org.apache.pulsar.functions.proto.Function; import org.apache.pulsar.functions.proto.InstanceCommunication; -import org.apache.pulsar.functions.utils.ComponentTypeUtils; import org.apache.pulsar.functions.utils.FunctionCommon; import org.apache.pulsar.functions.utils.FunctionConfigUtils; -import org.apache.pulsar.functions.worker.PulsarWorkerService; import javax.ws.rs.core.Response; import java.io.File; @@ -578,14 +576,12 @@ private static Class[] extractTypeArgs(final FunctionConfig functionConfig, return null; } ClassLoader clsLoader = FunctionConfigUtils.validate(functionConfig, componentPackageFile); - if (functionConfig.getRuntime() == FunctionConfig.Runtime.JAVA) { - if (clsLoader != null) { - try { - typeArgs = FunctionCommon.getFunctionTypes(functionConfig, clsLoader); - } catch (ClassNotFoundException | NoClassDefFoundError e) { - throw new IllegalArgumentException( - String.format("Function class %s must be in class path", functionConfig.getClassName()), e); - } + if (functionConfig.getRuntime() == FunctionConfig.Runtime.JAVA && clsLoader != null) { + try { + typeArgs = FunctionCommon.getFunctionTypes(functionConfig, clsLoader); + } catch (ClassNotFoundException | NoClassDefFoundError e) { + throw new IllegalArgumentException( + String.format("Function class %s must be in class path", functionConfig.getClassName()), e); } } return typeArgs; diff --git a/mesh-worker-service/src/test/java/io/functionmesh/compute/rest/api/FunctionsImplTest.java b/mesh-worker-service/src/test/java/io/functionmesh/compute/rest/api/FunctionsImplTest.java index 13fd646fb..3d77f5cf8 100644 --- a/mesh-worker-service/src/test/java/io/functionmesh/compute/rest/api/FunctionsImplTest.java +++ b/mesh-worker-service/src/test/java/io/functionmesh/compute/rest/api/FunctionsImplTest.java @@ -44,7 +44,6 @@ import okhttp3.ResponseBody; import okhttp3.internal.http.RealResponseBody; import org.apache.commons.codec.digest.DigestUtils; -import org.apache.pulsar.client.admin.Packages; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.client.admin.Tenants; @@ -62,7 +61,6 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; diff --git a/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java b/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java index 8288f6abc..c39bf9281 100644 --- a/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java +++ b/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java @@ -23,7 +23,6 @@ import io.functionmesh.compute.functions.models.V1alpha1FunctionSpec; import io.functionmesh.compute.testdata.Generate; -import java.io.File; import java.util.Collections; import java.util.function.Supplier; @@ -69,7 +68,6 @@ public void testCreateV1alpha1FunctionFromFunctionConfig() { String jar = "/pulsar/function-executable"; MeshWorkerService meshWorkerService = PowerMockito.mock(MeshWorkerService.class); - Supplier meshWorkerServiceSupplier = () -> meshWorkerService; CustomObjectsApi customObjectsApi = PowerMockito.mock(CustomObjectsApi.class); PowerMockito.when(meshWorkerService.getCustomObjectsApi()).thenReturn(customObjectsApi); WorkerConfig workerConfig = PowerMockito.mock(WorkerConfig.class); From 26008ede59926610f407f884ab42d2918aa73b83 Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Mon, 23 Aug 2021 21:12:12 +0800 Subject: [PATCH 7/8] address codacy --- .../java/io/functionmesh/compute/util/FunctionsUtilTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java b/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java index c39bf9281..085ed1e8f 100644 --- a/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java +++ b/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java @@ -114,7 +114,6 @@ public void testCreateFunctionConfigFromV1alpha1Function() { String kind = "Function"; MeshWorkerService meshWorkerService = PowerMockito.mock(MeshWorkerService.class); - Supplier meshWorkerServiceSupplier = () -> meshWorkerService; CustomObjectsApi customObjectsApi = PowerMockito.mock(CustomObjectsApi.class); PowerMockito.when(meshWorkerService.getCustomObjectsApi()).thenReturn(customObjectsApi); WorkerConfig workerConfig = PowerMockito.mock(WorkerConfig.class); From 23cf5fd99d7eeea6e872dc3fab8857f821d233c2 Mon Sep 17 00:00:00 2001 From: Rui Fu Date: Mon, 23 Aug 2021 21:17:15 +0800 Subject: [PATCH 8/8] address codacy --- .../java/io/functionmesh/compute/util/FunctionsUtilTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java b/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java index 085ed1e8f..d173ed9c6 100644 --- a/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java +++ b/mesh-worker-service/src/test/java/io/functionmesh/compute/util/FunctionsUtilTest.java @@ -24,7 +24,6 @@ import io.functionmesh.compute.testdata.Generate; import java.util.Collections; -import java.util.function.Supplier; import io.kubernetes.client.openapi.apis.CustomObjectsApi; import okhttp3.Response;