IK.AM

@making's tech note


Create a CronJob Workload with Tanzu Application Platform

🗃 {Dev/CaaS/Kubernetes/TAP}
🏷 Kubernetes 🏷 Cartographer 🏷 kind 🏷 Tanzu 🏷 TAP 
🗓 Updated at 2023-04-18T01:04:56Z  🗓 Created at 2023-04-18T00:59:32Z   🇯🇵 Original entry

⚠️ The content of this article is not supported by VMware. Any issues arising from the content of this article are your responsibility and please do not contact VMware Support.

For Tanzu Application Platform (as of 1.4), as stated in the documentation here , Out of the Box (OOTB) Supply Chains supports the following Workload Types:

  • type=web ... intended for scalable web applications. A Knative Service resource is created. Scale to Zero, Zero to N are supported.
  • type=server ... intended for traditional web applications. K8s standard Deployment and Service resources are created.
  • type=worker ... intended for background applications processing queues. A K8s standard Deployment is created.

This time, add type=cronjob here so that K8s standard CronJob resource can be created.

TAP's workload is managed by Cartographer. Customizing the resources created by Workload is typically done by adding a ClusterSupplyChain resource. However, when creating a Supply Chain from scratch, it is necessary to define the components that make up the Supply Chain like bellow

  • Retrieve the source code
  • Scan the vulnerability of the source code vulnerability
  • Test the source code
  • Build the container image
  • Scan the vulnerability of the container image
  • Create manifests
  • Package and push the manifests
  • etc ... (Tutorial here)

Redefining what is defined in TAP's OOTB Supply Chain is a pain.

TAP allows you to customize only the "Create manifests" part in the OOTB Supply Chain. Documentation here. The documentation gives an example of creating an Ingress resource in addition to a resource of type=server. You can define the manifest template you want the supply chain to generate in Cartographer's ClusterConfigTemplate.

Try it in the environment created in this article.

table of contents

Defining a ClusterConfigTemplate

Define CronJob's template in ClusterConfigTemplate as follows:

cat <<EOF > cronjob-template.yaml
apiVersion: carto.run/v1alpha1
kind: ClusterConfigTemplate
metadata:
  name: cronjob-template
spec:
  configPath: .data
  lifecycle: mutable
  ytt: |
    #@ load("@ytt:data", "data")
    #@ load("@ytt:yaml", "yaml")
    
    #@ def merge_labels(fixed_values):
    #@   labels = {}
    #@   if hasattr(data.values.workload.metadata, "labels"):
    #@     labels.update(data.values.workload.metadata.labels)
    #@   end
    #@   labels.update(fixed_values)
    #@   return labels
    #@ end

    #@ def update_config(config):
    #@   values = {}
    #@   values.update(config)
    #@   spec = dict(values["spec"])
    #@   spec.update({"restartPolicy": data.values.params.restartPolicy if hasattr(data.values.params, "restartPolicy") else "Never"})
    #@   workload = dict(spec["containers"][0])
    #@   if hasattr(data.values.params, "command"):
    #@     workload.update({"command": data.values.params.command})
    #@   end
    #@   if hasattr(data.values.params, "args"):
    #@     workload.update({"args": data.values.params.args})
    #@   end
    #@   spec["containers"][0] = workload
    #@   values["spec"] = spec
    #@   return values
    #@ end
    
    #@ def delivery():
    apiVersion: batch/v1
    kind: CronJob
    metadata:
      name: #@ data.values.workload.metadata.name
      annotations:
        kapp.k14s.io/update-strategy: "fallback-on-replace"
        ootb.apps.tanzu.vmware.com/servicebinding-workload: "true"
        kapp.k14s.io/change-rule: "upsert after upserting servicebinding.io/ServiceBindings"
      labels: #@ merge_labels({ "app.kubernetes.io/component": "run", "carto.run/workload-name": data.values.workload.metadata.name })
    spec:
      schedule: #@ data.values.params.schedule if hasattr(data.values.params, "schedule") else "0 15 * * *"
      successfulJobsHistoryLimit: 1
      failedJobsHistoryLimit: 3
      concurrencyPolicy: Forbid
      jobTemplate:
        metadata:
          labels: #@ data.values.config.metadata.labels
        spec:
          backoffLimit: 0
          template: #@ update_config(data.values.config)
    #@ end
    
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: #@ data.values.workload.metadata.name + "-cronjob"
      labels: #@ merge_labels({ "app.kubernetes.io/component": "config" })
    data:
      delivery.yml: #@ yaml.encode(delivery())
EOF

Instead of K8s standard CronJob, If you want to use an extended scheduler like Argo Workflows or Furiko, you can also change this ClusterConfigTemplate.

This CronJob manifest will eventually be deployed by the Deliverable, so the Service Account that manages the Deliverable must have permission to create the CronJob. Labeling the ClusterRole apps.tanzu.vmware.com/aggregate-to-deliverable: "true" binds it to the Service Account that manages the Deliverable.

cat <<EOF > deliverable-with-cronjob.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: deliverable-with-cronjob
  labels:
    apps.tanzu.vmware.com/aggregate-to-deliverable: "true"
rules:
- apiGroups:
  - batch
  resources:
  - cronjobs
  verbs:
  - get
  - list
  - watch
  - create
  - patch
  - update
  - delete
  - deletecollection
EOF

apply these.

# In case of Muti Cluster configuration, in Build Cluster
kubectl apply -f cronjob-template.yaml
# In case of Muti Cluster configuration, in Run Cluster
kubectl apply -f deliverable-with-cronjob.yml

Finally, add a setting to add Workload Type to tap-values.yaml.

ootb_supply_chain_basic: # if supply_chain value is basic
# or ootb_supply_chain_testing: if supply_chain value is testing
# or ootb_supply_chain_testing_scanning: if supply_chain value is testing_scanning
  supported_workloads:
  - type: web
    cluster_config_template_name: config-template
  - type: server
    cluster_config_template_name: server-template
  - type: worker
    cluster_config_template_name: worker-template
  - type: cronjob
    cluster_config_template_name: cronjob-template # <---

Reflect changes in tap-values.yaml.

tanzu package installed update -n tap-install tap --values-file tap-values.yaml

Once the update is complete, you can see that the source-to-url Supply Chain has been added with type=cronjob.

$ tanzu apps cluster-supply-chain get source-to-url 
---
# source-to-url: Ready
---
Supply Chain Selectors
   TYPE          KEY                                   OPERATOR   VALUE
   expressions   apps.tanzu.vmware.com/workload-type   In         web
   expressions   apps.tanzu.vmware.com/workload-type   In         server
   expressions   apps.tanzu.vmware.com/workload-type   In         worker
   expressions   apps.tanzu.vmware.com/workload-type   In         cronjob

Currently, the Service Binding function cannot be used. Bind to CronJob is not supported.

(Optional) Change settings with Overlays

You may not like the procedure of updating the TAP by running the bellow commands after installing TAP as it is not declarative installation of TAP.

kubectl apply -f cronjob-template.yaml
kubectl apply -f deliverable-with-cronjob.yml

ClusterConfigTemplate and ClusterRole can be created at the same time when installing/updating TAP with tanzu package install or tanzu package installed update.

In this case, create a ClusterConfigTemplate and a ClusterRole as overlays as follows:

# In case of Muti Cluster configuration, in Build Cluster
kubectl -n tap-install create secret generic ootb-templates-cronjob-template \
  -o yaml \
  --dry-run=client \
  --from-file=cronjob-template.yaml \
  | kubectl apply -f-
# In case of Muti Cluster configuration, in Run Cluster
kubectl -n tap-install create secret generic tap-auth-deliverable-with-cronjob \
  -o yaml \
  --dry-run=client \
  --from-file=deliverable-with-cronjob.yml \
  | kubectl apply -f-

Add the following settings to tap-values.yaml.

package_overlays:
- name: ootb-templates
  secrets:
  - name: ootb-templates-cronjob-template # in Build Cluster for Muti Cluster configuration
- name: tap-auth
  secrets:
  - name: tap-auth-deliverable-with-cronjob # in Run Cluster for Muti Cluster configuration

For first-time installation,

tanzu package install tap -p tap.tanzu.vmware.com -v 1.4.2 --values-file tap-values.yaml -n tap-install

For update

tanzu package installed update -n tap-install tap --values-file tap-values.yaml

Now you can use type=cronjob.

Creating CronJob Workloads

Let's create various CronJobs.

A Simple CronJob

Let's create a sample from Kubernetes official documentation.

Create a Workload to create a CronJob with the following command. This time, --image specifies the container image to be run by CronJob.

tanzu apps workload apply \
  -n demo \
  --type cronjob \
  --image ghcr.io/making/busybox \
  --param-yaml 'command=["/bin/sh", "-c", "date; echo Hello from TAP"]' \
  --param schedule="* * * * *" \
  --app cronjob-demo \
  cronjob-demo

As in the official document, when running with --image busybox:1.28, the following error occurred in the ImageRepository resource. unable to pull image "busybox:1.28": Expected an Image but got: application/vnd.docker.distribution.manifest.list.v2+json To fix the problem, I did a relocation like this:

docker pull busybox
docker tag busybox ghcr.io/making/busybox
docker push busybox

After a while a CronJob will be created and more Jobs will be executed.

$ tanzu apps workload get cronjob-demo --namespace demo
📡 Overview
   name:        cronjob-demo
   type:        cronjob
   namespace:   demo

💾 Source
   type:    image
   image:   ghcr.io/making/busybox

📦 Supply Chain
   name:   basic-image-to-url

   NAME               READY   HEALTHY   UPDATED   RESOURCE
   image-provider     True    True      10m       imagerepositories.source.apps.tanzu.vmware.com/cronjob-demo
   config-provider    True    True      10m       podintents.conventions.carto.run/cronjob-demo
   app-config         True    True      10m       configmaps/cronjob-demo-cronjob
   service-bindings   True    True      10m       configmaps/cronjob-demo-with-claims
   api-descriptors    True    True      10m       configmaps/cronjob-demo-with-api-descriptors
   config-writer      True    True      10m       runnables.carto.run/cronjob-demo-config-writer

🚚 Delivery
   name:   delivery-basic

   NAME              READY   HEALTHY   UPDATED   RESOURCE
   source-provider   True    True      9m34s     imagerepositories.source.apps.tanzu.vmware.com/cronjob-demo-delivery
   deployer          True    True      60s       apps.kappctrl.k14s.io/cronjob-demo

💬 Messages
   No messages found.

🛶 Pods
   NAME                                   READY   STATUS      RESTARTS   AGE
   cronjob-demo-28018560-zdp85            0/1     Completed   0          4s
   cronjob-demo-config-writer-l68rw-pod   0/1     Completed   0          10m

To see logs: "tanzu apps workload tail cronjob-demo --namespace demo --timestamp --since 1h"

Check the created CronJobs and Jobs.

$ kubectl get cronjob,job -n demo 
NAME                         SCHEDULE    SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob.batch/cronjob-demo   * * * * *   False     0        23s             81s

NAME                              COMPLETIONS   DURATION   AGE
job.batch/cronjob-demo-28018560   1/1           7s         23s

Check the logs. time is displayed.

$ kubectl logs -n demo cronjob-demo-28018560-zdp85 
Mon Apr 10 08:00:04 UTC 2023
Hello from TAP

Since the schedule is * * * * *, this Job will run every minute.

After confirming, delete the workload.

tanzu apps workload delete -n demo cronjob-demo

A CronJob that just runs curl

Create a Workload to create a CronJob with the following command.

tanzu apps workload apply \
  -n demo \
  --type cronjob \
  --image ghcr.io/making/curl \
  --param-yaml 'command=["curl"]' \
  --param-yaml 'args=["-s", "https://httpbin.org/get"]' \
  --param schedule="* * * * *" \
  --app hello-curl \
  hello-curl

After a while a CronJob will be created and more Jobs will be executed.

$ tanzu apps workload get hello-curl --namespace demo
📡 Overview
   name:        hello-curl
   type:        cronjob
   namespace:   demo

💾 Source
   type:    image
   image:   ghcr.io/making/curl

📦 Supply Chain
   name:   basic-image-to-url

   NAME               READY   HEALTHY   UPDATED   RESOURCE
   image-provider     True    True      106s      imagerepositories.source.apps.tanzu.vmware.com/hello-curl
   config-provider    True    True      102s      podintents.conventions.carto.run/hello-curl
   app-config         True    True      102s      configmaps/hello-curl-cronjob
   service-bindings   True    True      102s      configmaps/hello-curl-with-claims
   api-descriptors    True    True      102s      configmaps/hello-curl-with-api-descriptors
   config-writer      True    True      91s       runnables.carto.run/hello-curl-config-writer

🚚 Delivery
   name:   delivery-basic

   NAME              READY   HEALTHY   UPDATED   RESOURCE
   source-provider   True    True      44s       imagerepositories.source.apps.tanzu.vmware.com/hello-curl-delivery
   deployer          True    True      42s       apps.kappctrl.k14s.io/hello-curl

💬 Messages
   No messages found.

🛶 Pods
   NAME                                 READY   STATUS      RESTARTS   AGE
   hello-curl-28018564-2rlgc            0/1     Completed   0          11s
   hello-curl-config-writer-k76bd-pod   0/1     Completed   0          101s

To see logs: "tanzu apps workload tail hello-curl --namespace demo --timestamp --since 1h"

Check the created CronJobs and Jobs.

$ kubectl get cronjob,job -n demo 
NAME                       SCHEDULE    SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob.batch/hello-curl   * * * * *   False     0        42s             76s

NAME                            COMPLETIONS   DURATION   AGE
job.batch/hello-curl-28018564   1/1           8s         42s

Check the logs. The execution result of curl is output.

$ kubectl logs -n demo hello-curl-28018564-2rlgc 
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/8.0.1-DEV", 
    "X-Amzn-Trace-Id": "Root=1-6433c2f5-5f649b191de9f1ea4e83fb8f"
  }, 
  "origin": "220.242.176.124", 
  "url": "https://httpbin.org/get"
}

After confirming, delete the workload.

tanzu apps workload delete -n demo hello-curl

CronJob to run Python Script

Next, create a container image to be run by CronJob from the source code. Convert the Python code from https://github.com/making/hello-python into a container image with Cloud Native Buildpacks and run CronJob.

Create a Workload to create a CronJob with the following command. This time, specify the URL of the source code with --git-repo.

tanzu apps workload apply \
   -n demo \
   --type cronjob \
   --git-repo https://github.com/making/hello-python\
   --git-branch main\
   --param schedule="* * * * *" \
   --app hello-python \
   --label apps.tanzu.vmware.com/has-tests=true \
   hello-python

After a while a CronJob will be created and more Jobs will be executed.

$ tanzu apps workload get hello-python --namespace demo 
📡 Overview
   name:        hello-python
   type:        cronjob
   namespace:   demo

💾 Source
   type:     git
   url:      https://github.com/making/hello-python
   branch:   main

📦 Supply Chain
   name:   source-to-url

   NAME               READY   HEALTHY   UPDATED   RESOURCE
   source-provider    True    True      4m58s     gitrepositories.source.toolkit.fluxcd.io/hello-python
   image-provider     True    True      3m58s     images.kpack.io/hello-python
   config-provider    True    True      3m52s     podintents.conventions.carto.run/hello-python
   app-config         True    True      3m52s     configmaps/hello-python-cronjob
   service-bindings   True    True      3m52s     configmaps/hello-python-with-claims
   api-descriptors    True    True      3m52s     configmaps/hello-python-with-api-descriptors
   config-writer      True    True      3m38s     runnables.carto.run/hello-python-config-writer

🚚 Delivery
   name:   delivery-basic

   NAME              READY   HEALTHY   UPDATED   RESOURCE
   source-provider   True    True      2m54s     imagerepositories.source.apps.tanzu.vmware.com/hello-python-delivery
   deployer          True    True      2m52s     apps.kappctrl.k14s.io/hello-python

💬 Messages
   No messages found.

🛶 Pods
   NAME                                   READY   STATUS      RESTARTS   AGE
   hello-python-28018837-25db8            0/1     Completed   0          32s
   hello-python-build-1-build-pod         0/1     Completed   0          4m57s
   hello-python-config-writer-ch8z4-pod   0/1     Completed   0          3m50s

Check the created CronJobs and Jobs.

$ kubectl get cronjob,job -n demo 
NAME                         SCHEDULE    SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob.batch/hello-python   * * * * *   False     0        45s             3m6s

NAME                              COMPLETIONS   DURATION   AGE
job.batch/hello-python-28018837   1/1           5s         45s

Check the logs.

$ kubectl logs -n demo hello-python-28018837-25db8 
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.28.2", 
    "X-Amzn-Trace-Id": "Root=1-643402ed-0e6ea59220b36d3636bd2a41"
  }, 
  "origin": "220.242.176.124", 
  "url": "https://httpbin.org/get"
}

After confirming, delete the workload.

tanzu apps workload delete -n demo hello-python

CronJob to run Spring Batch Job

Next, the batch processing source code https://github.com/making/billing-job implemented by Spring Batch will be converted into a container image with Cloud Native Buildpacks and executed by CronJob.

Create a Workload to create a CronJob with the following command.

tanzu apps workload apply \
  -n demo \
  --type cronjob \
  --git-repo https://github.com/making/billing-job \
  --git-branch master \
  --param schedule="0 15 * * *" \
  --app billing-job \
  --label apps.tanzu.vmware.com/has-tests=true \
  --build-env BP_JVM_VERSION=17 \
  --env BPL_JVM_THREAD_COUNT=8 \
  --env JAVA_TOOL_OPTIONS=-Dmanagement.health.probes.enabled=false \
  billing-job

--env JAVA_TOOL_OPTIONS=-Dmanagement.health.probes.enabled=false is added to disable the probe settings that are automatically added when using Spring Boot Actuator.
Documentation here

A CronJob will be created after a while. Since we have scheduled it to run at 00:00 JST, the Job has not yet been created.

 $ tanzu apps workload get billing-job --namespace demo
📡 Overview
   name:        billing-job
   type:        cronjob
   namespace:   demo

💾 Source
   type:     git
   url:      https://github.com/making/billing-job
   branch:   master

📦 Supply Chain
   name:   source-to-url

   NAME               READY   HEALTHY   UPDATED   RESOURCE
   source-provider    True    True      4m51s     gitrepositories.source.toolkit.fluxcd.io/billing-job
   image-provider     True    True      3m39s     images.kpack.io/billing-job
   config-provider    True    True      3m34s     podintents.conventions.carto.run/billing-job
   app-config         True    True      3m34s     configmaps/billing-job-cronjob
   service-bindings   True    True      3m33s     configmaps/billing-job-with-claims
   api-descriptors    True    True      3m33s     configmaps/billing-job-with-api-descriptors
   config-writer      True    True      3m17s     runnables.carto.run/billing-job-config-writer

🚚 Delivery
   name:   delivery-basic

   NAME              READY   HEALTHY   UPDATED   RESOURCE
   source-provider   True    True      2m48s     imagerepositories.source.apps.tanzu.vmware.com/billing-job-delivery
   deployer          True    True      2m46s     apps.kappctrl.k14s.io/billing-job

💬 Messages
   No messages found.

🛶 Pods
   NAME                                  READY   STATUS      RESTARTS   AGE
   billing-job-build-1-build-pod         0/1     Completed   0          4m52s
   billing-job-config-writer-mlhff-pod   0/1     Completed   0          3m33s

To see logs: "tanzu apps workload tail billing-job --namespace demo --timestamp --since 1h"

Check the created CronJob.

$ kubectl get cronjob,job -n demo 
NAME                        SCHEDULE     SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob.batch/billing-job   0 15 * * *   False     0        <none>          3m48s

Create a Job manually.

kubectl create job -n demo --from cronjob/billing-job test-run

Check the created CronJobs and Jobs.

$ kubectl get cronjob,job -n demo 
NAME                        SCHEDULE     SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob.batch/billing-job   0 15 * * *   False     0        <none>          5m12s

NAME                 COMPLETIONS   DURATION   AGE
job.batch/test-run   1/1           13s        26s

Check the logs.

$ kubectl logs -n demo test-run-c9r5d 
Setting Active Processor Count to 8
Calculating JVM memory based on 4528180K available memory
For more information on this calculation, see https://paketo.io/docs/reference/java-reference/#memory-calculator
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx4191438K -XX:MaxMetaspaceSize=72549K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 4528180K, Thread Count: 8, Loaded Class Count: 10395, Headroom: 0%)
Enabling Java Native Memory Tracking
Adding 124 container CA certificates to JVM truststore
Spring Cloud Bindings Enabled
Picked up JAVA_TOOL_OPTIONS: -Dmanagement.health.probes.enabled="false" -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -XX:+ExitOnOutOfMemoryError -XX:ActiveProcessorCount=8 -XX:MaxDirectMemorySize=10M -Xmx4191438K -XX:MaxMetaspaceSize=72549K -XX:ReservedCodeCacheSize=240M -Xss1M -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics -Dorg.springframework.cloud.bindings.boot.enable=true

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.0.5)

2023-04-10T16:38:45.371Z  INFO 1 --- [           main] lol.maki.billing.BillingJobApplication   : Starting BillingJobApplication v0.0.1-SNAPSHOT using Java 17.0.6 with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)
2023-04-10T16:38:45.376Z  INFO 1 --- [           main] lol.maki.billing.BillingJobApplication   : No active profile set, falling back to 1 default profile: "default"
2023-04-10T16:38:46.207Z  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2023-04-10T16:38:46.436Z  INFO 1 --- [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:b918459b-19ff-41d4-8f50-d482bba0dee8 user=SA
2023-04-10T16:38:46.440Z  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2023-04-10T16:38:46.829Z  INFO 1 --- [           main] lol.maki.billing.BillingJobApplication   : Started BillingJobApplication in 1.968 seconds (process running for 2.419)
2023-04-10T16:38:46.832Z  INFO 1 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2023-04-10T16:38:46.844Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:46.845Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]
2023-04-10T16:38:46.860Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:46.860Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE I1 where I1.JOB_NAME = ? and I1.JOB_INSTANCE_ID in (SELECT max(I2.JOB_INSTANCE_ID) from BATCH_JOB_INSTANCE I2 where I2.JOB_NAME = ?)]
2023-04-10T16:38:46.868Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:46.869Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]
2023-04-10T16:38:46.871Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:46.871Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]
2023-04-10T16:38:46.872Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:46.872Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]
2023-04-10T16:38:46.878Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:46.879Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [INSERT into BATCH_JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY, VERSION) values (?, ?, ?, ?)]
2023-04-10T16:38:46.889Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:46.889Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [INSERT into BATCH_JOB_EXECUTION(JOB_EXECUTION_ID, JOB_INSTANCE_ID, START_TIME, END_TIME, STATUS, EXIT_CODE, EXIT_MESSAGE, VERSION, CREATE_TIME, LAST_UPDATED) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]
2023-04-10T16:38:46.892Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing SQL batch update [INSERT into BATCH_JOB_EXECUTION_PARAMS(JOB_EXECUTION_ID, PARAMETER_NAME, PARAMETER_TYPE, PARAMETER_VALUE, IDENTIFYING) values (?, ?, ?, ?, ?)] with a batch size of 100
2023-04-10T16:38:46.893Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [INSERT into BATCH_JOB_EXECUTION_PARAMS(JOB_EXECUTION_ID, PARAMETER_NAME, PARAMETER_TYPE, PARAMETER_VALUE, IDENTIFYING) values (?, ?, ?, ?, ?)]
2023-04-10T16:38:46.901Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:46.902Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [INSERT INTO BATCH_JOB_EXECUTION_CONTEXT (SHORT_CONTEXT, SERIALIZED_CONTEXT, JOB_EXECUTION_ID) VALUES(?, ?, ?)]
2023-04-10T16:38:46.904Z  INFO 1 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=BillingJob]] launched with the following parameters: [{'run.id':'{value=1, type=class java.lang.Long, identifying=true}'}]
2023-04-10T16:38:46.922Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:46.923Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT VERSION FROM BATCH_JOB_EXECUTION WHERE JOB_EXECUTION_ID=?]
2023-04-10T16:38:46.928Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:46.928Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT COUNT(*) FROM BATCH_JOB_EXECUTION WHERE JOB_EXECUTION_ID = ?]
2023-04-10T16:38:46.932Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:46.932Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [UPDATE BATCH_JOB_EXECUTION set START_TIME = ?, END_TIME = ?,  STATUS = ?, EXIT_CODE = ?, EXIT_MESSAGE = ?, VERSION = ?, CREATE_TIME = ?, LAST_UPDATED = ? where JOB_EXECUTION_ID = ? and VERSION = ?]
2023-04-10T16:38:46.936Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:46.936Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT  SE.STEP_EXECUTION_ID, SE.STEP_NAME, SE.START_TIME, SE.END_TIME, SE.STATUS, SE.COMMIT_COUNT, SE.READ_COUNT, SE.FILTER_COUNT, SE.WRITE_COUNT, SE.EXIT_CODE, SE.EXIT_MESSAGE, SE.READ_SKIP_COUNT, SE.WRITE_SKIP_COUNT, SE.PROCESS_SKIP_COUNT, SE.ROLLBACK_COUNT, SE.LAST_UPDATED, SE.VERSION, SE.CREATE_TIME, JE.JOB_EXECUTION_ID, JE.START_TIME, JE.END_TIME, JE.STATUS, JE.EXIT_CODE, JE.EXIT_MESSAGE, JE.CREATE_TIME, JE.LAST_UPDATED, JE.VERSION from BATCH_JOB_EXECUTION JE join BATCH_STEP_EXECUTION SE      on SE.JOB_EXECUTION_ID = JE.JOB_EXECUTION_ID where JE.JOB_INSTANCE_ID = ?      and SE.STEP_NAME = ? order by SE.CREATE_TIME desc, SE.STEP_EXECUTION_ID desc]
2023-04-10T16:38:46.940Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:46.940Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT COUNT(*)  from BATCH_JOB_EXECUTION JE JOIN BATCH_STEP_EXECUTION SE       on SE.JOB_EXECUTION_ID = JE.JOB_EXECUTION_ID where JE.JOB_INSTANCE_ID = ?      and SE.STEP_NAME = ?]
2023-04-10T16:38:46.942Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:46.942Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [INSERT into BATCH_STEP_EXECUTION(STEP_EXECUTION_ID, VERSION, STEP_NAME, JOB_EXECUTION_ID, START_TIME, END_TIME, STATUS, COMMIT_COUNT, READ_COUNT, FILTER_COUNT, WRITE_COUNT, EXIT_CODE, EXIT_MESSAGE, READ_SKIP_COUNT, WRITE_SKIP_COUNT, PROCESS_SKIP_COUNT, ROLLBACK_COUNT, LAST_UPDATED, CREATE_TIME) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]
2023-04-10T16:38:46.943Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:46.943Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [INSERT INTO BATCH_STEP_EXECUTION_CONTEXT (SHORT_CONTEXT, SERIALIZED_CONTEXT, STEP_EXECUTION_ID) VALUES(?, ?, ?)]
2023-04-10T16:38:46.945Z  INFO 1 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [BilliProcessing]
2023-04-10T16:38:46.946Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:46.947Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [UPDATE BATCH_STEP_EXECUTION set START_TIME = ?, END_TIME = ?, STATUS = ?, COMMIT_COUNT = ?, READ_COUNT = ?, FILTER_COUNT = ?, WRITE_COUNT = ?, EXIT_CODE = ?, EXIT_MESSAGE = ?, VERSION = ?, READ_SKIP_COUNT = ?, PROCESS_SKIP_COUNT = ?, WRITE_SKIP_COUNT = ?, ROLLBACK_COUNT = ?, LAST_UPDATED = ? where STEP_EXECUTION_ID = ? and VERSION = ?]
2023-04-10T16:38:46.948Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:46.948Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT VERSION FROM BATCH_JOB_EXECUTION WHERE JOB_EXECUTION_ID=?]
2023-04-10T16:38:46.955Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:46.955Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [UPDATE BATCH_STEP_EXECUTION_CONTEXT SET SHORT_CONTEXT = ?, SERIALIZED_CONTEXT = ? WHERE STEP_EXECUTION_ID = ?]
2023-04-10T16:38:47.023Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing SQL batch update [INSERT INTO BILL_STATEMENTS (id, first_name, last_name, minutes, data_usage,bill_amount) VALUES (?, ?, ?, ?, ?, ?)]
2023-04-10T16:38:47.023Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [INSERT INTO BILL_STATEMENTS (id, first_name, last_name, minutes, data_usage,bill_amount) VALUES (?, ?, ?, ?, ?, ?)]
2023-04-10T16:38:47.027Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:47.028Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [UPDATE BATCH_STEP_EXECUTION_CONTEXT SET SHORT_CONTEXT = ?, SERIALIZED_CONTEXT = ? WHERE STEP_EXECUTION_ID = ?]
2023-04-10T16:38:47.029Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:47.029Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [UPDATE BATCH_STEP_EXECUTION set START_TIME = ?, END_TIME = ?, STATUS = ?, COMMIT_COUNT = ?, READ_COUNT = ?, FILTER_COUNT = ?, WRITE_COUNT = ?, EXIT_CODE = ?, EXIT_MESSAGE = ?, VERSION = ?, READ_SKIP_COUNT = ?, PROCESS_SKIP_COUNT = ?, WRITE_SKIP_COUNT = ?, ROLLBACK_COUNT = ?, LAST_UPDATED = ? where STEP_EXECUTION_ID = ? and VERSION = ?]
2023-04-10T16:38:47.030Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:47.030Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT VERSION FROM BATCH_JOB_EXECUTION WHERE JOB_EXECUTION_ID=?]
2023-04-10T16:38:47.034Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing SQL batch update [INSERT INTO BILL_STATEMENTS (id, first_name, last_name, minutes, data_usage,bill_amount) VALUES (?, ?, ?, ?, ?, ?)]
2023-04-10T16:38:47.035Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [INSERT INTO BILL_STATEMENTS (id, first_name, last_name, minutes, data_usage,bill_amount) VALUES (?, ?, ?, ?, ?, ?)]
2023-04-10T16:38:47.037Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:47.038Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [UPDATE BATCH_STEP_EXECUTION_CONTEXT SET SHORT_CONTEXT = ?, SERIALIZED_CONTEXT = ? WHERE STEP_EXECUTION_ID = ?]
2023-04-10T16:38:47.039Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:47.039Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [UPDATE BATCH_STEP_EXECUTION set START_TIME = ?, END_TIME = ?, STATUS = ?, COMMIT_COUNT = ?, READ_COUNT = ?, FILTER_COUNT = ?, WRITE_COUNT = ?, EXIT_CODE = ?, EXIT_MESSAGE = ?, VERSION = ?, READ_SKIP_COUNT = ?, PROCESS_SKIP_COUNT = ?, WRITE_SKIP_COUNT = ?, ROLLBACK_COUNT = ?, LAST_UPDATED = ? where STEP_EXECUTION_ID = ? and VERSION = ?]
2023-04-10T16:38:47.040Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:47.040Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT VERSION FROM BATCH_JOB_EXECUTION WHERE JOB_EXECUTION_ID=?]
2023-04-10T16:38:47.042Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:47.042Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [UPDATE BATCH_STEP_EXECUTION_CONTEXT SET SHORT_CONTEXT = ?, SERIALIZED_CONTEXT = ? WHERE STEP_EXECUTION_ID = ?]
2023-04-10T16:38:47.045Z  INFO 1 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [BilliProcessing] executed in 99ms
2023-04-10T16:38:47.045Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:47.045Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [UPDATE BATCH_STEP_EXECUTION set START_TIME = ?, END_TIME = ?, STATUS = ?, COMMIT_COUNT = ?, READ_COUNT = ?, FILTER_COUNT = ?, WRITE_COUNT = ?, EXIT_CODE = ?, EXIT_MESSAGE = ?, VERSION = ?, READ_SKIP_COUNT = ?, PROCESS_SKIP_COUNT = ?, WRITE_SKIP_COUNT = ?, ROLLBACK_COUNT = ?, LAST_UPDATED = ? where STEP_EXECUTION_ID = ? and VERSION = ?]
2023-04-10T16:38:47.046Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:47.046Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT VERSION FROM BATCH_JOB_EXECUTION WHERE JOB_EXECUTION_ID=?]
2023-04-10T16:38:47.048Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:47.048Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [UPDATE BATCH_JOB_EXECUTION_CONTEXT SET SHORT_CONTEXT = ?, SERIALIZED_CONTEXT = ? WHERE JOB_EXECUTION_ID = ?]
2023-04-10T16:38:47.051Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:47.052Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT VERSION FROM BATCH_JOB_EXECUTION WHERE JOB_EXECUTION_ID=?]
2023-04-10T16:38:47.055Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2023-04-10T16:38:47.056Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT COUNT(*) FROM BATCH_JOB_EXECUTION WHERE JOB_EXECUTION_ID = ?]
2023-04-10T16:38:47.057Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2023-04-10T16:38:47.057Z DEBUG 1 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [UPDATE BATCH_JOB_EXECUTION set START_TIME = ?, END_TIME = ?,  STATUS = ?, EXIT_CODE = ?, EXIT_MESSAGE = ?, VERSION = ?, CREATE_TIME = ?, LAST_UPDATED = ? where JOB_EXECUTION_ID = ? and VERSION = ?]
2023-04-10T16:38:47.069Z  INFO 1 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=BillingJob]] completed with the following parameters: [{'run.id':'{value=1, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 129ms
2023-04-10T16:38:47.082Z  INFO 1 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2023-04-10T16:38:47.088Z  INFO 1 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

Native Memory Tracking:
...

After confirming, delete the workload.

tanzu apps workload delete -n demo billing-job

CronJobs can now be created from TAP. Customizing TAP opens up new possibilities.

FYI, the workload for backing up this blog and saving it to S3 is here.


✒️️ Edit  ⏰ History  🗑 Delete