Home > database >  Building a spring boot azure function with pipeline fails with following error: Failed to get Azure
Building a spring boot azure function with pipeline fails with following error: Failed to get Azure

Time:06-28

I am relatively new to azure functions and I am trying to create an azure function in java using spring boot that is triggered by an azure event hub message. I used an example I found here and changed the relevant code from HTTP trigger to event hub trigger.

Building it locally and deploying it to azure via the maven plugin works without any issues but soon as I try to have this code built by an azure pipeline I get the following error:

[INFO] Step 8 of 8: Installing function extensions if needed
[WARNING] Failed to get Azure Functions Core Tools version locally
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  03:17 min
[INFO] Finished at: 2022-06-25T13:39:48Z
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal com.microsoft.***:***-functions-maven-plugin:1.14.2:package (package-functions) on project cutcloud-token-service: Local Azure Functions Core Tools does not exist or is too old to support function extension installation, skip package phase. To install or update it, see: https://aka.ms/azfunc-install -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
The process '/usr/bin/mvn' failed with exit code 1

my pipeline code:

trigger:
  branches:
    include:
      - master

stages:
  - stage: Build
    jobs:
      - job: Build
        steps:
          - task: MavenAuthenticate@0
            inputs:
              mavenServiceConnections: 'nexus'

          - task: Maven@3
            inputs:
              mavenPomFile: 'pom.xml'
              publishJUnitResults: true
              testResultsFiles: '**/surefire-reports/TEST-*.xml'
              javaHomeOption: 'JDKVersion'
              jdkVersionOption: 11
              mavenVersionOption: 'Default'
              mavenAuthenticateFeed: false
              effectivePomSkip: false
              sonarQubeRunAnalysis: false
              goals: clean package

and here is the pom.xml mostly like in the example with some additional libraries needed by my business logic:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>hello</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Hello Spring Function on Azure</name>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.6</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>

    <azure.functions.java.library.version>2.0.0</azure.functions.java.library.version>
    <azure.functions.maven.plugin.version>1.17.0</azure.functions.maven.plugin.version>

    <!-- customize those two properties. The functionAppName should be unique across Azure -->
    <functionResourceGroup>my-spring-function-resource-group</functionResourceGroup>
    <functionAppName>my-spring-function</functionAppName>

    <functionAppRegion>westeurope</functionAppRegion>
    <stagingDirectory>${project.build.directory}/azure-functions/${functionAppName}</stagingDirectory>
    <start-class>com.example.DemoApplication</start-class>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-function-adapter-azure</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-function-webflux</artifactId>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
    </dependency>
    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
    </dependency>
    <dependency>
      <groupId>com.microsoft.azure.sdk.iot</groupId>
      <artifactId>iot-service-client</artifactId>
      <version>1.33.2</version>
    </dependency>
    <dependency>
      <groupId>de.digital.asap.cloud</groupId>
      <artifactId>cloud-messages</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.9.1</version>
    </dependency>
    <dependency>
      <groupId>net.gsadev.crypto</groupId>
      <artifactId>crypto-lib</artifactId>
      <version>1.0.0</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>

    <!-- Test -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.gatling.highcharts</groupId>
      <artifactId>gatling-charts-highcharts</artifactId>
      <version>3.7.6</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-function-dependencies</artifactId>
        <version>3.2.3</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>com.microsoft.azure.functions</groupId>
        <artifactId>azure-functions-java-library</artifactId>
        <version>${azure.functions.java.library.version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>com.microsoft.azure</groupId>
          <artifactId>azure-functions-maven-plugin</artifactId>
          <version>${azure.functions.maven.plugin.version}</version>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.2.0</version>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-dependency-plugin</artifactId>
          <version>3.3.0</version>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-clean-plugin</artifactId>
        </plugin>
        <plugin>
          <groupId>io.gatling</groupId>
          <artifactId>gatling-maven-plugin</artifactId>
          <version>4.1.5</version>
          <configuration>
            <includes>
              <include>com.example.loadtest.*</include>
            </includes>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>

    <plugins>
      <plugin>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-functions-maven-plugin</artifactId>
        <configuration>
          <resourceGroup>${functionResourceGroup}</resourceGroup>
          <appName>${functionAppName}</appName>
          <region>${functionAppRegion}</region>
          <runtime>
            <os>linux</os>
            <javaVersion>11</javaVersion>
          </runtime>
          <appSettings>
            <!-- Run Azure Function from package file by default -->
            <property>
              <name>FUNCTIONS_EXTENSION_VERSION</name>
              <value>~4</value>
            </property>
            <property>
              <name>FUNCTIONS_WORKER_RUNTIME</name>
              <value>java</value>
            </property>
          </appSettings>
        </configuration>
        <executions>
          <execution>
            <id>package-functions</id>
            <goals>
              <goal>package</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <executions>
          <execution>
            <id>copy-resources</id>
            <phase>package</phase>
            <goals>
              <goal>copy-resources</goal>
            </goals>
            <configuration>
              <overwrite>true</overwrite>
              <outputDirectory>
                ${project.build.directory}/azure-functions/${functionAppName}
              </outputDirectory>
              <resources>
                <resource>
                  <directory>${project.basedir}/src/main/azure
                  </directory>
                  <includes>
                    <include>**</include>
                  </includes>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
          <execution>
            <id>copy-dependencies</id>
            <phase>prepare-package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>${stagingDirectory}/lib</outputDirectory>
              <overWriteReleases>false</overWriteReleases>
              <overWriteSnapshots>false</overWriteSnapshots>
              <overWriteIfNewer>true</overWriteIfNewer>
              <includeScope>runtime</includeScope>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <!--Remove obj folder generated by .NET SDK in maven clean-->
      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <filesets>
            <fileset>
              <directory>obj</directory>
            </fileset>
          </filesets>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

  <repositories>
    <repository>
      <id>gsadev.nexus</id>
      <name>Internal GSIDEV Repository</name>
      <url>https://nexus.gsadev.net/repository/maven-public/</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </repository>
    <repository>
      <id>spring-snapshots</id>
      <name>Spring Snapshots</name>
      <url>https://repo.spring.io/libs-snapshot-local</url>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
      <releases>
        <enabled>false</enabled>
      </releases>
    </repository>
    <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/libs-milestone-local</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
    <repository>
      <id>spring-releases</id>
      <name>Spring Releases</name>
      <url>https://repo.spring.io/release</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <id>spring-snapshots</id>
      <name>Spring Snapshots</name>
      <url>https://repo.spring.io/libs-snapshot-local</url>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
      <releases>
        <enabled>false</enabled>
      </releases>
    </pluginRepository>
    <pluginRepository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/libs-milestone-local</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </pluginRepository>
    <pluginRepository>
      <id>spring-releases</id>
      <name>Spring Releases</name>
      <url>https://repo.spring.io/libs-release-local</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </pluginRepository>
  </pluginRepositories>
</project>

What I found strange is that the error does not ocurre if I simply remove the event hub annotation from the trigger method without doing any other changes to the code and/or pom:

  @FunctionName("createAndSendToken")
  public void run(
//      @EventHubTrigger(name = "apiTokenRequests",
//          eventHubName = "api-key-requests",
//          connection = "EventhubConnectionString",
//          consumerGroup = "digiapp",
//          cardinality = Cardinality.ONE)
      String payload,
      final ExecutionContext context
  ) {
    context.getLogger().info("EventHub trigger function execution started");
    context.getLogger().info("payload:"   payload);
    KeyResponse keyResponse = handleRequest(new KeyRequest(payload), context);
    context.getLogger().info("EventHub trigger function execution finished with status "   keyResponse.getStatus());
  }

Even more strange: I have an earlier version of the function derieved from an other example (which I don't find any more) that is not using spring. Here the code with the eventhub trigger can be build by an azure pipeline without any issues. the pom for that function looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>de.digital.asap.azure</groupId>
  <artifactId>cutcloud-token-service</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Cutcloud Token Service</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <azure.functions.maven.plugin.version>1.14.2</azure.functions.maven.plugin.version>
    <azure.functions.java.library.version>1.4.2</azure.functions.java.library.version>
    <functionAppName>cutcloud-token-service</functionAppName>
    <stagingDirectory>${project.build.directory}/azure-functions/${functionAppName}</stagingDirectory>
  </properties>

  <repositories>
    <repository>
      <id>gsadev.nexus</id>
      <name>Internal GSADEV Repository</name>
      <url>https://nexus.gsadev.net/repository/maven-public/</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </repository>
  </repositories>

  <dependencies>
    <dependency>
      <groupId>com.microsoft.azure.functions</groupId>
      <artifactId>azure-functions-java-library</artifactId>
      <version>${azure.functions.java.library.version}</version>
    </dependency>

    <!--    <dependency>-->
    <!--      <groupId>com.microsoft.azure</groupId>-->
    <!--      <artifactId>applicationinsights-core</artifactId>-->
    <!--      <version>2.6.3</version>-->
    <!--    </dependency>-->

    <dependency>
      <groupId>com.microsoft.azure.sdk.iot</groupId>
      <artifactId>iot-service-client</artifactId>
      <version>1.33.2</version>
    </dependency>

    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp</artifactId>
      <version>4.9.2</version>
    </dependency>

    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>1.15</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.12.5</version>
    </dependency>

    <dependency>
      <groupId>net.gsadev.crypto</groupId>
      <artifactId>crypto-lib</artifactId>
      <version>1.0.0</version>
    </dependency>

    <dependency>
      <groupId>de.digital.asab.cloud</groupId>
      <artifactId>cloud-messages</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.22</version>
      <optional>true</optional>
    </dependency>

<!--    <dependency>-->
<!--      <groupId>ch.qos.logback</groupId>-->
<!--      <artifactId>logback-core</artifactId>-->
<!--      <version>1.2.7</version>-->
<!--    </dependency>-->

<!--    <dependency>-->
<!--      <groupId>ch.qos.logback</groupId>-->
<!--      <artifactId>logback-classic</artifactId>-->
<!--      <version>1.2.7</version>-->
<!--    </dependency>-->

    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.9.1</version>
    </dependency>

<!--    <dependency>-->
<!--      <groupId>org.slf4j</groupId>-->
<!--      <artifactId>slf4j-api</artifactId>-->
<!--      <version>1.7.32</version>-->
<!--    </dependency>-->

    <!-- Test -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.8.1</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>4.0.0</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-functions-maven-plugin</artifactId>
        <version>${azure.functions.maven.plugin.version}</version>
        <configuration>
          <appName>${functionAppName}</appName>
          <resourceGroup>gsadev-sandbox</resourceGroup>
          <appServicePlanName>TestServicePlan</appServicePlanName>
          <region>westeurope</region>
          <!-- function pricingTier, default to be consumption if not specified -->
          <!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-pricing-tiers for all valid values -->
          <!-- <pricingTier></pricingTier> -->
          <disableAppInsights>false</disableAppInsights>
          <runtime>
            <!-- runtime os, could be windows, linux or docker-->
            <os>linux</os>
            <javaVersion>8</javaVersion>
            <!-- for docker function, please set the following parameters -->
            <!-- <image>[hub-user/]repo-name[:tag]</image> -->
            <!-- <serverId></serverId> -->
            <!-- <registryUrl></registryUrl>  -->
          </runtime>
          <appSettings>
            <property>
              <name>FUNCTIONS_EXTENSION_VERSION</name>
              <value>~3</value>
            </property>
          </appSettings>
        </configuration>
        <executions>
          <execution>
            <id>package-functions</id>
            <goals>
              <goal>package</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.1.0</version>
        <executions>
          <execution>
            <id>copy-resources</id>
            <phase>package</phase>
            <goals>
              <goal>copy-resources</goal>
            </goals>
            <configuration>
              <overwrite>true</overwrite>
              <outputDirectory>${stagingDirectory}</outputDirectory>
              <resources>
                <resource>
                  <directory>${project.basedir}</directory>
                  <includes>
                    <include>host.json</include>
                    <include>local.settings.json</include>
                  </includes>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>3.1.1</version>
        <executions>
          <execution>
            <id>copy-dependencies</id>
            <phase>prepare-package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>${stagingDirectory}/lib</outputDirectory>
              <overWriteReleases>false</overWriteReleases>
              <overWriteSnapshots>false</overWriteSnapshots>
              <overWriteIfNewer>true</overWriteIfNewer>
              <includeScope>runtime</includeScope>
              <excludeArtifactIds>azure-functions-java-library</excludeArtifactIds>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <!--Remove obj folder generated by .NET SDK in maven clean-->
      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <filesets>
            <fileset>
              <directory>obj</directory>
            </fileset>
          </filesets>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

The code and annotation for the older function trigger is the same as in the new code that can't be built for some reason.

I already tried different things like changing the version of the maven plugin, the azure libraries, the function version, and the extension bundle versions. etc. I also tried different build/agent images:

pool:
  vmImage: 'ubuntu-latest'

and

pool:
  vmImage: 'ubuntu-22.04'

but nothing helped. I am out of ideas. Can anyone help, please?

regards Christian

CodePudding user response:

Because of the reasons mentioned above, I am still not convinced that this is the best solution but following the comment of @khmarbaise, I fixed my problem by installing the necessary tools manually into the build agent before calling the actual build step:

          - task: Npm@1
            inputs:
              #this is needed so that the function extension install in the maven task doesn't fail
              command: 'custom'
              customCommand: 'install -g azure-functions-core-tools@3 --unsafe-perm true'
  • Related