Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion eng/ci/public-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,32 @@ pr:
include:
- dev

# Pipeline-level parameters surfaced in the ADO "Run pipeline" dialog so we can
# build a custom branch of azure-functions-java-additions (e.g. a fork) before
# the worker's mvn build. Defaults to a no-op so normal PRs continue to resolve
# the library from Maven Central.
#
# TEMPORARY: defaults are flipped to build the fork branch
# (ahmedmuhsin/azure-functions-java-additions @ feat/http-response-bodystream)
# so GitHub-triggered PR CI for #877 picks up the unpublished
# `azure-functions-java-core-library:1.4.0-SNAPSHOT`. Revert to the upstream
# defaults (buildAdditionsFromSource=false, Azure repo, dev branch) once the
# companion PR (Azure/azure-functions-java-additions#55) is merged and a
# matching library version is published to Maven Central.
parameters:
- name: buildAdditionsFromSource
displayName: 'Build azure-functions-java-additions from source (instead of resolving from Maven Central)'
type: boolean
default: true
- name: additionsRepoUrl
displayName: 'Git URL for azure-functions-java-additions (used only when buildAdditionsFromSource is true)'
type: string
default: 'https://github.com/ahmedmuhsin/azure-functions-java-additions.git'
- name: additionsBranch
displayName: 'Branch of azure-functions-java-additions to build (used only when buildAdditionsFromSource is true)'
type: string
default: 'feat/http-response-bodystream'

resources:
repositories:
- repository: 1es
Expand Down Expand Up @@ -51,24 +77,37 @@ extends:
- stage: Build
jobs:
- template: /eng/ci/templates/jobs/build.yml@self
parameters:
buildAdditionsFromSource: ${{ parameters.buildAdditionsFromSource }}
additionsRepoUrl: ${{ parameters.additionsRepoUrl }}
additionsBranch: ${{ parameters.additionsBranch }}

- stage: TestWindows
dependsOn: []
jobs:
- template: /eng/ci/templates/jobs/run-emulated-tests-windows.yml@self
parameters:
poolName: 1es-pool-azfunc-public
buildAdditionsFromSource: ${{ parameters.buildAdditionsFromSource }}
additionsRepoUrl: ${{ parameters.additionsRepoUrl }}
additionsBranch: ${{ parameters.additionsBranch }}

- stage: TestLinux
dependsOn: []
jobs:
- template: /eng/ci/templates/jobs/run-emulated-tests-linux.yml@self
parameters:
poolName: 1es-pool-azfunc-public
buildAdditionsFromSource: ${{ parameters.buildAdditionsFromSource }}
additionsRepoUrl: ${{ parameters.additionsRepoUrl }}
additionsBranch: ${{ parameters.additionsBranch }}

- stage: TestDocker
dependsOn: []
jobs:
- template: /eng/ci/templates/jobs/run-docker-tests-linux.yml@self
parameters:
poolName: 1es-pool-azfunc-public
poolName: 1es-pool-azfunc-public
buildAdditionsFromSource: ${{ parameters.buildAdditionsFromSource }}
additionsRepoUrl: ${{ parameters.additionsRepoUrl }}
additionsBranch: ${{ parameters.additionsBranch }}
15 changes: 15 additions & 0 deletions eng/ci/templates/jobs/build.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
parameters:
- name: buildAdditionsFromSource
type: boolean
default: false
- name: additionsRepoUrl
type: string
default: 'https://github.com/Azure/azure-functions-java-additions.git'
- name: additionsBranch
type: string
default: 'dev'

jobs:
- job: "Build"
displayName: 'Build java worker'
Expand All @@ -14,6 +25,10 @@ jobs:
- pwsh: |
java -version
displayName: 'Check default java version'
- ${{ if eq(parameters.buildAdditionsFromSource, true) }}:
- pwsh: |
./installAdditionsLocally.ps1 -AdditionsRepoUrl '${{ parameters.additionsRepoUrl }}' -AdditionsBranch '${{ parameters.additionsBranch }}'
displayName: 'Install azure-functions-java-additions from source'
- pwsh: |
mvn clean package
displayName: 'Build java worker'
14 changes: 14 additions & 0 deletions eng/ci/templates/jobs/run-docker-tests-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ parameters:
- name: poolName
type: string
default: ''
- name: buildAdditionsFromSource
type: boolean
default: false
- name: additionsRepoUrl
type: string
default: 'https://github.com/Azure/azure-functions-java-additions.git'
- name: additionsBranch
type: string
default: 'dev'

jobs:
- job: "TestDocker"
Expand Down Expand Up @@ -51,6 +60,11 @@ jobs:
pip install -e dockertests/azure-functions-test-kit
displayName: 'Install Python dependencies'

- ${{ if eq(parameters.buildAdditionsFromSource, true) }}:
- pwsh: |
./installAdditionsLocally.ps1 -AdditionsRepoUrl '${{ parameters.additionsRepoUrl }}' -AdditionsBranch '${{ parameters.additionsBranch }}'
displayName: 'Install azure-functions-java-additions from source'

- pwsh: |
./package-pipeline.ps1 -outputDir 'java-worker' -skipNuget
displayName: 'Package Java worker'
Expand Down
13 changes: 13 additions & 0 deletions eng/ci/templates/jobs/run-emulated-tests-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ parameters:
- name: poolName
type: string
default: ''
- name: buildAdditionsFromSource
type: boolean
default: false
- name: additionsRepoUrl
type: string
default: 'https://github.com/Azure/azure-functions-java-additions.git'
- name: additionsBranch
type: string
default: 'dev'

jobs:
- job: "TestLinux"
Expand Down Expand Up @@ -83,6 +92,10 @@ jobs:
docker compose -f emulatedtests/utils/docker-compose.yml pull
docker compose -f emulatedtests/utils/docker-compose.yml up -d
displayName: 'Install Azurite and Start Emulators'
- ${{ if eq(parameters.buildAdditionsFromSource, true) }}:
- pwsh: |
./installAdditionsLocally.ps1 -AdditionsRepoUrl '${{ parameters.additionsRepoUrl }}' -AdditionsBranch '${{ parameters.additionsBranch }}'
displayName: 'Install azure-functions-java-additions from source'
- pwsh: |
if ("$(isTag)"){
$buildNumber="$(Build.SourceBranchName)"
Expand Down
13 changes: 13 additions & 0 deletions eng/ci/templates/jobs/run-emulated-tests-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ parameters:
- name: poolName
type: string
default: ''
- name: buildAdditionsFromSource
type: boolean
default: false
- name: additionsRepoUrl
type: string
default: 'https://github.com/Azure/azure-functions-java-additions.git'
- name: additionsBranch
type: string
default: 'dev'

jobs:
- job: "TestWindows"
Expand Down Expand Up @@ -78,6 +87,10 @@ jobs:
mkdir azurite
azurite --silent --location azurite --debug azurite\debug.log &
displayName: 'Install and Run Azurite'
- ${{ if eq(parameters.buildAdditionsFromSource, true) }}:
- pwsh: |
./installAdditionsLocally.ps1 -AdditionsRepoUrl '${{ parameters.additionsRepoUrl }}' -AdditionsBranch '${{ parameters.additionsBranch }}'
displayName: 'Install azure-functions-java-additions from source'
- pwsh: |
if ("$(isTag)"){
$buildNumber="$(Build.SourceBranchName)"
Expand Down
100 changes: 74 additions & 26 deletions installAdditionsLocally.ps1
Original file line number Diff line number Diff line change
@@ -1,28 +1,76 @@
# Variables for first repository
$repoUrl1 = 'https://github.com/Azure/azure-functions-java-additions.git'
$branchName1 = 'dev'
$repoName1 = 'azure-functions-java-additions'

# Clone the first repository
git clone $repoUrl1

# Change directory to the cloned repository
Set-Location $repoName1

# Checkout the desired branch
git checkout $branchName1

# Detect OS and execute build accordingly
if ($IsWindows) {
# Run the batch script (mvnBuild.bat)
& "..\mvnBuildAdditions.bat"
} else {
# Extract and explicitly invoke the mvn command from mvnBuild.bat
$mvnCommand = Get-Content "../mvnBuildAdditions.bat" | Where-Object { $_ -match '^mvn\s+' }
if ($null -ne $mvnCommand) {
# Execute the extracted mvn command explicitly as a single line
bash -c "$mvnCommand"
} else {
Write-Error "No mvn command found in mvnBuild.bat."
[CmdletBinding()]
param(
[string]$AdditionsRepoUrl = 'https://github.com/Azure/azure-functions-java-additions.git',
[string]$AdditionsBranch = 'dev',
[bool]$SkipTests = $true
)

$ErrorActionPreference = 'Stop'

$repoName = 'azure-functions-java-additions'
$workerRoot = $PSScriptRoot
$cloneDir = Join-Path $workerRoot $repoName
$mvnBuildScript = Join-Path $workerRoot 'mvnBuildAdditions.bat'

# CI bootstrap only needs artifacts installed into the local Maven cache.
# Skipping tests avoids JDK-matrix-specific test compilation failures in additions.
$skipTestArgs = if ($SkipTests) { ' -Dmaven.test.skip=true' } else { '' }

Write-Host "Installing $repoName from $AdditionsRepoUrl (branch: $AdditionsBranch)"
Write-Host "Clone destination: $cloneDir"

# Make the clone idempotent for re-runs.
if (Test-Path $cloneDir) {
Write-Host "Removing existing $cloneDir"
Remove-Item -Path $cloneDir -Recurse -Force
}

Push-Location $workerRoot
try {
git clone --branch $AdditionsBranch --single-branch $AdditionsRepoUrl
if ($LASTEXITCODE -ne 0) { throw "git clone failed for $AdditionsRepoUrl ($AdditionsBranch)" }

Push-Location $repoName
try {
# spotbugs-maven-plugin:3.1.6 bundles groovy-3.0.0-alpha-3 which crashes at class-load
# time on JDK 17+ (ExceptionInInitializerError in org.codehaus.groovy.vmplugin.v7.Java7).
# This happens before Maven can check -Dspotbugs.skip=true, so the skip flag is useless.
# Work around by building additions with Java 8 when JAVA_HOME_8_X64 is available (always
# set on ADO agents by the JavaToolInstaller pre-step). Falls back to current JAVA_HOME
# on developer machines that don't have that variable set.
$savedJavaHome = $env:JAVA_HOME
$savedPath = $env:PATH
$java8Home = $env:JAVA_HOME_8_X64
if ($java8Home -and (Test-Path $java8Home)) {
Write-Host "Temporarily using Java 8 (JAVA_HOME_8_X64=$java8Home) for additions install"
Write-Host " (avoids spotbugs-maven-plugin:3.1.6 Groovy incompatibility on JDK 17+)"
$env:JAVA_HOME = $java8Home
$env:PATH = (Join-Path $java8Home 'bin') + [System.IO.Path]::PathSeparator + $env:PATH
} else {
Write-Host "JAVA_HOME_8_X64 not set; using current JAVA_HOME: $env:JAVA_HOME"
}

try {
# Extract the Maven command from mvnBuildAdditions.bat so we can append extra flags.
$mvnCommand = Get-Content $mvnBuildScript | Where-Object { $_ -match '^mvn\s+' }
if ($null -eq $mvnCommand) {
throw "No mvn command found in $mvnBuildScript"
}

if ($IsWindows) {
& cmd.exe /c "$mvnCommand$skipTestArgs"
} else {
bash -c "$mvnCommand$skipTestArgs"
}
if ($LASTEXITCODE -ne 0) { throw "additions maven command failed" }
} finally {
# Restore JAVA_HOME/PATH regardless of success or failure.
$env:JAVA_HOME = $savedJavaHome
$env:PATH = $savedPath
}
} finally {
Pop-Location
}
} finally {
Pop-Location
}
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<azure.functions.java.core.library.version>1.3.0</azure.functions.java.core.library.version>
<azure.functions.java.core.library.version>1.4.0-SNAPSHOT</azure.functions.java.core.library.version>
<azure.functions.java.spi>1.1.0</azure.functions.java.spi>
<azure.functions.java.sdktypes>1.0.2</azure.functions.java.sdktypes>
<azure.functions.java.library.version>2.2.0</azure.functions.java.library.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,11 @@ private Constants(){}
public static final String JAVA_ENABLE_OPENTELEMETRY = "JAVA_ENABLE_OPENTELEMETRY";
public static final String JAVA_APPLICATIONINSIGHTS_ENABLE_TELEMETRY = "JAVA_APPLICATIONINSIGHTS_ENABLE_TELEMETRY";
public static final String JAVA_ENABLE_SDK_TYPES = "JAVA_ENABLE_SDK_TYPES";
/**
* If set to "true" (case-insensitive), the worker will NOT start the
* embedded HTTP proxy server and will NOT advertise the {@code HttpUri}
* capability. Useful as an escape hatch if the proxy path causes problems.
* Default: unset (proxy enabled).
*/
public static final String FUNCTIONS_JAVA_DISABLE_HTTP_PROXY = "FUNCTIONS_JAVA_DISABLE_HTTP_PROXY";
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@

import com.microsoft.azure.functions.worker.broker.*;
import com.microsoft.azure.functions.worker.handler.*;
import com.microsoft.azure.functions.worker.http.HttpInvocationCoordinator;
import com.microsoft.azure.functions.worker.http.HttpProxyServer;
import com.microsoft.azure.functions.worker.http.ProxyConfig;
import com.microsoft.azure.functions.worker.reflect.*;
import com.microsoft.azure.functions.rpc.messages.*;

Expand All @@ -36,19 +39,28 @@ public JavaWorkerClient(IApplication app) {
this.peer = new AtomicReference<>(null);
this.handlerSuppliers = new HashMap<>();
this.classPathProvider = new FactoryClassLoader().createClassLoaderProvider();

this.httpInvocationCoordinator = new HttpInvocationCoordinator();
this.httpProxyServer = httpProxyEnabled() ? new HttpProxyServer(ProxyConfig.defaults()) : null;

this.addHandlers();
}

private static boolean httpProxyEnabled() {
String value = System.getenv(Constants.FUNCTIONS_JAVA_DISABLE_HTTP_PROXY);
return !Boolean.parseBoolean(value);
}

@PostConstruct
private void addHandlers() {
JavaFunctionBroker broker = new JavaFunctionBroker(classPathProvider);

this.handlerSuppliers.put(StreamingMessage.ContentCase.WORKER_INIT_REQUEST, () -> new WorkerInitRequestHandler(broker));

this.handlerSuppliers.put(StreamingMessage.ContentCase.WORKER_INIT_REQUEST,
() -> new WorkerInitRequestHandler(broker, this.httpProxyServer, this.httpInvocationCoordinator));
this.handlerSuppliers.put(StreamingMessage.ContentCase.WORKER_WARMUP_REQUEST, WorkerWarmupHandler::new);
this.handlerSuppliers.put(StreamingMessage.ContentCase.FUNCTION_ENVIRONMENT_RELOAD_REQUEST, () -> new FunctionEnvironmentReloadRequestHandler(broker));
this.handlerSuppliers.put(StreamingMessage.ContentCase.FUNCTION_LOAD_REQUEST, () -> new FunctionLoadRequestHandler(broker));
this.handlerSuppliers.put(StreamingMessage.ContentCase.INVOCATION_REQUEST, () -> new InvocationRequestHandler(broker));
this.handlerSuppliers.put(StreamingMessage.ContentCase.INVOCATION_REQUEST,
() -> new InvocationRequestHandler(broker, this.httpInvocationCoordinator));
this.handlerSuppliers.put(StreamingMessage.ContentCase.WORKER_STATUS_REQUEST, WorkerStatusRequestHandler::new);
this.handlerSuppliers.put(StreamingMessage.ContentCase.WORKER_TERMINATE, WorkerTerminateRequestHandler::new);
}
Expand All @@ -68,6 +80,15 @@ void logToHost(LogRecord record, String invocationId) {

@Override
public void close() throws Exception {
// Stop accepting HTTP proxy requests before tearing down the gRPC peer
// so in-flight HTTP handlers can drain on completion futures cleanly.
if (this.httpProxyServer != null) {
try {
this.httpProxyServer.close();
} catch (Exception ex) {
logger.log(Level.WARNING, "Failed to close HTTP proxy server cleanly", ex);
}
}
this.peer.get().close();
this.peer.set(null);
this.channel.shutdownNow();
Expand Down Expand Up @@ -143,6 +164,8 @@ private synchronized void send(String requestId, MessageHandler<?, ?> marshaller
private final AtomicReference<StreamingMessagePeer> peer;
private final Map<StreamingMessage.ContentCase, Supplier<MessageHandler<?, ?>>> handlerSuppliers;
private final ClassLoaderProvider classPathProvider;
private final HttpInvocationCoordinator httpInvocationCoordinator;
private final HttpProxyServer httpProxyServer;

/**
* @param functionsUri Host endpoint URI, or null for legacy startup args that only provide host and port.
Expand Down
Loading
Loading