Push an app with multiple buildpacks and cflinuxfs4 stack leveraging the cf-java-client
search cancel

Push an app with multiple buildpacks and cflinuxfs4 stack leveraging the cf-java-client

book

Article ID: 298132

calendar_today

Updated On:

Products

VMware Tanzu Application Service for VMs

Issue/Introduction

This Knowledge Base (KB) article covers the scenario for leveraging the cf-java-client to push an app consisting of multiple buildpacks and a stack that differs from the cloud controller configured default. 


The following behavior was discovered:

  • TAS configured default stack is cflinuxfs3
  • Leveraging the cf-java-client, but not its updated v3 support, push an app manifest that includes 2 buildpacks and a cflinuxfs4 stack
  • A buildpack in that manifest does not have a version associated with cflinuxfs3 stack


It can result in the following error:

CF-UnprocessableEntity(10008): Buildpack “java_buildpack_offline461” for stack “cflinuxfs3" must be an existing admin buildpack or a valid git URI

org.cloudfoundry.client.v3.ClientV3Exception: CF-UnprocessableEntity(10008): Buildpack “java_buildpack_offline461” for stack “cflinuxfs3" must be an existing admin buildpack or a valid git URI


The reference to cflinuxfs3 in the above error is not expected, as cflinuxfs4 is specified in the manifest. 

The specific API call that is being made which results in the above error:

PATCH "/v3/apps/<APP_GUID>"

with PATCH data:

{"lifecycle":{"data":{"buildpacks":["appdbuildpack","java_buildpack_offline461"]},"type":"buildpack"}}


When comparing this PATCH data to the same API call from a cf CLI cf push of the same manifest, the data sent from the client is:

{"lifecycle":{"type":"buildpack","data":{"buildpacks":["appdbuildpack","java_buildpack_offline461"],"stack":"cflinuxfs4"}}}


When the cf-java-client adds the buildpack to the application, the CAPI call (PATCH "/v3/apps/<APP_GUID>")  does not include the desired stack, and due to this, cloud controller looks for the buildpack using the default stack. When cloud controller can not find the buildpack with the default stack (cflinuxfs3) then it results in the observed error. 

Environment

Tanzu Application services for VMs

Resolution

When pushing an application that includes multiple buildpacks and a stack that differs from the configured platform default, then cf-java-client v5.10.0 + must be used along with code updates to leverage the new v3 features. 

To demonstrate this, consider the following environment/example:

$ cf buildpacks
Getting buildpacks as admin...

position   name                        stack        enabled   locked   filename
1          java_buildpack_offline461   cflinuxfs4   true      false    java-buildpack-offline-v4.61.0.zip
2          staticfile_buildpack        cflinuxfs3   true      false    staticfile_buildpack-cached-cflinuxfs3-v1.6.0.zip
3          appdbuildpack4              cflinuxfs4   true      false    appdynamics_buildpack-v23.9.0-59.zip
4          java_buildpack_offline      cflinuxfs3   true      false    java-buildpack-offline-cflinuxfs3-v4.56.zip
5          java_buildpack_offline      cflinuxfs4   true      false    java-buildpack-offline-cflinuxfs4-v4.56.zip
6          ruby_buildpack              cflinuxfs3   true      false    ruby_buildpack-cached-cflinuxfs3-v1.9.4.zip
7          ruby_buildpack              cflinuxfs4   true      false    ruby_buildpack-cached-cflinuxfs4-v1.9.4.zip
8          binary_buildpack            cflinuxfs3   true      false    binary_buildpack-cached-cflinuxfs3-v1.1.3.zip
9          dotnet_core_buildpack       cflinuxfs4   true      false    dotnet-core_buildpack-cached-cflinuxfs4-v2.4.9.zip
10         dotnet_core_buildpack       cflinuxfs3   true      false    dotnet-core_buildpack-cached-cflinuxfs3-v2.4.9.zip
11         appdbuildpack                            true      false    appdynamics_buildpack-v23.9.0-59.zip
12         nginx_buildpack             cflinuxfs3   true      false    nginx_buildpack-cached-cflinuxfs3-v1.2.1.zip
13         nodejs_buildpack            cflinuxfs3   true      false    nodejs_buildpack-cached-cflinuxfs3-v1.8.8.zip
14         nodejs_buildpack            cflinuxfs4   true      false    nodejs_buildpack-cached-cflinuxfs4-v1.8.8.zip
15         go_buildpack                cflinuxfs3   true      false    go_buildpack-cached-cflinuxfs3-v1.10.6.zip
16         go_buildpack                cflinuxfs4   true      false    go_buildpack-cached-cflinuxfs4-v1.10.6.zip
17         r_buildpack                 cflinuxfs3   true      false    r_buildpack-cached-cflinuxfs3-v1.2.0.zip
18         python_buildpack            cflinuxfs3   true      false    python_buildpack-cached-cflinuxfs3-v1.8.8.zip
19         python_buildpack            cflinuxfs4   true      false    python_buildpack-cached-cflinuxfs4-v1.8.8.zip
20         php_buildpack               cflinuxfs3   true      false    php_buildpack-cached-cflinuxfs3-v4.6.3.zip
21         binary_buildpack            cflinuxfs4   true      false    binary_buildpack-cached-cflinuxfs4-v1.1.3.zip
22         binary_buildpack            windows      true      false    binary_buildpack-cached-windows-v1.1.3.zip


cf-java-client v5.10.0 is being used to push the following manifest file:

applications:
- name: loggerapp
  memory: 1G
  buildpacks:
    - appdbuildpack
    - java_buildpack_offline461
  random-route: true
  stack: cflinuxfs4
  path: logger_app-0.0.1-SNAPSHOT.jar


With the following sample code:

public class CFops {

    DefaultCloudFoundryOperations ops;
    public CFops() {
        ApplicationContext context = new AnnotationConfigApplicationContext(Configuration.class);
        ops = (DefaultCloudFoundryOperations) context.getAutowireCapableBeanFactory().getBean("CFOperations");
    }

    public void pushApp(){
        Path manifestPath = Paths.get("assets/manifest.yml");
        ApplicationManifest manifest = ApplicationManifestUtils.read(manifestPath).get(0);
        PushApplicationManifestRequest pushApplicationManifestRequest = PushApplicationManifestRequest.builder().manifest(manifest).build();
        final Mono<Void> pushManifest = ops.applications().pushManifest(pushApplicationManifestRequest).doOnSubscribe(subscription -> System.out.println("pushing manifest for application."));
        pushManifest.block();
    }

    public void pushAppv3(){
        Path manifestPath = Paths.get("assets/manifest.yml");
        ManifestV3 manifest = ApplicationManifestUtilsV3.read(manifestPath);
        PushManifestV3Request pushApplicationManifestRequest = PushManifestV3Request.builder().manifest(manifest).build();
        final Mono<Void> pushManifest = ops.applications().pushManifestV3(pushApplicationManifestRequest).doOnSubscribe(subscription -> System.out.println("pushing manifest for application."));
        pushManifest.block();
    }
    
}


Note that pushApp() leverages the client's pushManifest method while pushAppv3() leverages the client's pushManifestV3 method.

  • pushApp() will result in an error if the configured platform default stack is cflinuxfs3. 
  • pushApp() will result in success if the configured platform default stack is cflinuxfs4. 
  • pushAppV3() will result in success if the configured platform default stack is cflinuxfs3. 
  • pushAppV3() will result in success if the configured platform default stack is cflinuxfs4.