TAP on GKE (1.0.0) : Part 3 – Deploy an application with testing and scanning in gcr repo

Reading Time: 5 mins

Overview

In this section, I will walk you through the steps required to deploy an application using the Tanzu Application Platform. Before moving further, please ensure below are completed:

  • Prepare set up is completed, If not done, then follow the steps in the post
  • Default kubeconfig context is set to the target Kubernetes cluster.
  • Tanzu Application Platform GUI is successfully installed, for more details, read here

Install Supply Chain with Testing and Scanning

This Cartographer Supply Chain ties a series of Kubernetes resources which, when working together, drives a developer-provided Workload from source code all the way to a Kubernetes configuration ready to be deployed to a cluster, having not only passed that source code through testing and vulnerability scanning, but also the container image produced. It includes below capabilities:

  • Watching a Git Repository or local directory for changes
  • Running tests from a developer-provided Tekton or Pipeline
  • Scanning the source code for known vulnerabilities using Grype
  • Building a container image out of the source code with Buildpacks
  • Scanning the image for known vulnerabilities
  • Applying operator-defined conventions to the container definition
  • Deploying the application to the same cluster

You can verify that you have the right set of supply chains installed by running the following command:

$  tanzu apps cluster-supply-chain list
NAME READY AGE LABEL SELECTOR
source-test-scan-to-url Ready 123m apps.tanzu.vmware.com/has-tests=true,apps.tanzu.vmware.com/workload-type=web

Setup Developer Namespaces to use Installed Packages

To create workload for your application using the registry credentials specified, run these commands to add credentials and Role-Based Access Control (RBAC) rules to the namespace that you plan to create the workload in:

# Syntax: 

kubectl create secret docker-registry registry-credentials --docker-server=REGISTRY-SERVER --docker-username=REGISTRY-USERNAME --docker-password=REGISTRY-PASSWORD -n YOUR-NAMESPACE

# Example: where <key>.json is the key file downloaded from GCP portal.

kubectl create secret docker-registry registry-credentials --docker-server=gcr.io --docker-username=_json_key --docker-password="$(cat <key>.json)" -n tap-install
  • Add placeholder read secrets, a service account, and RBAC rules to the developer namespace by running:
cat <<EOF | kubectl -n tap-install apply -f -

apiVersion: v1
kind: Secret
metadata:
name: tap-registry
annotations:
secretgen.carvel.dev/image-pull-secret: ""
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: e30K

---
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
secrets:
- name: registry-credentials
imagePullSecrets:
- name: registry-credentials
- name: tap-registry

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: default
rules:
- apiGroups: [source.toolkit.fluxcd.io]
resources: [gitrepositories]
verbs: ['*']
- apiGroups: [source.apps.tanzu.vmware.com]
resources: [imagerepositories]
verbs: ['*']
- apiGroups: [carto.run]
resources: [deliverables, runnables]
verbs: ['*']
- apiGroups: [kpack.io]
resources: [images]
verbs: ['*']
- apiGroups: [conventions.apps.tanzu.vmware.com]
resources: [podintents]
verbs: ['*']
- apiGroups: [""]
resources: ['configmaps']
verbs: ['*']
- apiGroups: [""]
resources: ['pods']
verbs: ['list']
- apiGroups: [tekton.dev]
resources: [taskruns, pipelineruns]
verbs: ['*']
- apiGroups: [tekton.dev]
resources: [pipelines]
verbs: ['list']
- apiGroups: [kappctrl.k14s.io]
resources: [apps]
verbs: ['*']
- apiGroups: [serving.knative.dev]
resources: ['services']
verbs: ['*']
- apiGroups: [servicebinding.io]
resources: ['servicebindings']
verbs: ['*']
- apiGroups: [services.apps.tanzu.vmware.com]
resources: ['resourceclaims']
verbs: ['*']
- apiGroups: [scanning.apps.tanzu.vmware.com]
resources: ['imagescans', 'sourcescans']
verbs: ['*']

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: default
subjects:
- kind: ServiceAccount
name: default

EOF

Tekton pipeline

A pipeline to be ran whenever the supply chain hits the stage of testing the source code. Save below into a yaml file for ex:  tekton-pipeline.yaml

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: developer-defined-tekton-pipeline
labels:
apps.tanzu.vmware.com/pipeline: test # (!) required
spec:
params:
- name: source-url # (!) required
- name: source-revision # (!) required
tasks:
- name: test
params:
- name: source-url
value: $(params.source-url)
- name: source-revision
value: $(params.source-revision)
taskSpec:
params:
- name: source-url
- name: source-revision
steps:
- name: test
image: gradle
script: |-
cd `mktemp -d`
wget -qO- $(params.source-url) | tar xvz
./mvnw test

Run the yaml file with following command in tap-install namespace:

kubectl apply -f tekton-pipeline.yaml -n tap-install
pipeline.tekton.dev/developer-defined-tekton-pipeline created

scan policy:

The ScanPolicy defines a set of rules to evaluate for a particular scan to consider the artifacts (image or source code) either compliant or not. When a ImageScan or SourceScan is created to run a scan, those reference a policy whose name must match the one below (scan-policy). Save below code into a yaml file, for ex: scanpolicy.yaml

apiVersion: scanning.apps.tanzu.vmware.com/v1beta1
kind: ScanPolicy
metadata:
name: scan-policy
spec:
regoFile: |
package policies

default isCompliant = false

# Accepted Values: "Critical", "High", "Medium", "Low", "Negligible", "UnknownSeverity"
violatingSeverities := ["Critical","High","UnknownSeverity"]
ignoreCVEs := []

contains(array, elem) = true {
array[_] = elem
} else = false { true }

isSafe(match) {
fails := contains(violatingSeverities, match.Ratings.Rating[_].Severity)
not fails
}

isSafe(match) {
ignore := contains(ignoreCVEs, match.Id)
ignore
}

isCompliant = isSafe(input.currentVulnerability)
  • Run the yaml file with following command in tap-install namespace:
kubectl apply -f scanpolicy.yaml -n tap-install
scanpolicy.scanning.apps.tanzu.vmware.com/scan-policy created

Image Secret:

Regardless of the supply chain that a Workload goes through, there must be a secret in the developer namespace. This secret contains the credentials to be passed to

# Where <key>.json is the file downloaded earlier: 

kubectl create secret docker-registry image-secret --docker-server=gcr.io --docker-username=_json_key --docker-password="$(cat <key>.json)" -n tap-install

ScanTemplate

  • Create a file named ootb-supply-chain-basic-values.yaml that specifies the corresponding values to the properties you want to change.
grype:
namespace: tap-install
targetImagePullSecret: registry-credentials
$  tanzu package install grype-scanner --package-name grype.scanning.apps.tanzu.vmware.com --version 1.0.0  --namespace tap-install -f ootb-supply-chain-basic-values.yaml
| Installing package 'grype.scanning.apps.tanzu.vmware.com'
/ Getting package metadata for 'grype.scanning.apps.tanzu.vmware.com'
| Creating service account 'grype-scanner-tap-install-sa'
/ Creating cluster admin role 'grype-scanner-tap-install-cluster-role'
/ Creating cluster role binding 'grype-scanner-tap-install-cluster-rolebinding'
| Creating secret 'grype-scanner-tap-install-values'
| Creating package resource
- Waiting for 'PackageInstall' reconciliation for 'grype-scanner'
\ 'PackageInstall' resource install status: Reconciling


Added installed package 'grype-scanner'

Deploy Application

To deploy your application, you must download an accelerator, upload it on your Git repository of choice, and run a CLI command. Let me take you through the steps of downloading an accelerator through TAP-GUI:

  • From the Tanzu Application Platform GUI portal, click on Accelerators on the left side of the navigation bar to see the list of available accelerators.
  • Locate the Tanzu Java Web App accelerator, which is a sample Spring Boot web app, and click on Choose button.

  • In the Generate Accelerators prompt, replace the default value dev.local in the prefix for container image registry field with the registry in the form of SERVER-NAME/REPO-NAME. The SERVER-NAME/REPO-NAME must match what was specified for registry as part of the installation values for ootb_supply_chain_basic. Click NEXT STEP, verify the provided information, and click CREATE.

  • After the Task Activity processes are complete, click on the DOWNLOAD ZIP FILE button

  • After downloading the zip file, expand it in a workspace directory and follow your preferred procedure for uploading the generated project files to a Git repository for your new project.
# Unzip the downloaded file and follow below process to push to git repo: 

reddye@reddye-a02 tanzu-java-web-app-demo % ls
LICENSE Tiltfile catalog-info.yaml mvnw pom.xml
README.md accelerator-log.md config mvnw.cmd src

# Initialize git

reddye@reddye-a02 tanzu-java-web-app-demo % git init

Initialized empty Git repository in /Users/reddye/Downloads/tanzu-java-web-app-demo/.git/

#Add all the files
git add *

#Commit
git commit -am "First commit"

git branch -M main

# Created a new repo named tanzu-java-web-app-demo in my github account and later executed below command:

git remote add origin https://github.com/Eknathreddy09/tanzu-java-web-app-demo.git

# Syntax: git config --global user.email "github account email"

git config --global user.email "[email protected]"

$ git push -u origin main
Username for 'https://github.com': eknathreddy09
Password for 'https://[email protected]':
Enumerating objects: 28, done.
Counting objects: 100% (28/28), done.
Delta compression using up to 16 threads
Compressing objects: 100% (19/19), done.
Writing objects: 100% (28/28), 15.36 KiB | 3.07 MiB/s, done.
Total 28 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/Eknathreddy09/tanzu-java-web-app-demo.git
* [new branch] main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.

# Login to Github account and verify the repo.

For this demo, instead of downloading from accelerator, I have a used a public version to test which is available here

  • Deploy the Tanzu Java Web App accelerator by running the tanzu apps workload create command:
$ tanzu apps workload create tanzu-java-web-app  --git-repo https://github.com/sample-accelerators/tanzu-java-web-app --git-branch main --type web --label apps.tanzu.vmware.com/has-tests=true --label app.kubernetes.io/part-of=tanzu-java-web-app  --type web -n tap-install --yes

# View the build and runtime logs for your app by running the tail command:

tanzu apps workload tail tanzu-java-web-app --since 10m --timestamp -n tap-install

# Get the status of application:

tanzu apps workload get tanzu-java-web-app -n tap-install
# tanzu-java-web-app: Ready
---
lastTransitionTime: "2022-01-17T06:04:43Z"
message: ""
reason: Ready
status: "True"
type: Ready

Workload pods
NAME STATE AGE
tanzu-java-web-app-00001-deployment-86c664cc87-6mmbj Running 8s
tanzu-java-web-app-build-1-build-pod Succeeded 56m
tanzu-java-web-app-config-writer-tnsfd-pod Succeeded 51m
tanzu-java-web-app-p6mb2-test-pod Succeeded 61m

Workload Knative Services
NAME READY URL
tanzu-java-web-app Ready http://tanzu-java-web-app.tap-install.example.com
  • Collect the External IP of Envoy service in name space: tanzu-system-ingress
kubectl get svc envoy -n tanzu-system-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
envoy LoadBalancer 10.32.4.176 35.244.7.37 80:32521/TCP,443:30574/TCP 103m
  • Add an entry in your local machine /etc/hosts with the IP collected above pointing to hostname: tanzu-java-web-app.tap-install.example.com

  • Access the url tanzu-java-web-app.tap-install.example.com and you should see result as below:

Verify the scan results:

#Source scan 

kubectl get sourcescan -n tap-install
NAME PHASE SCANNEDREVISION SCANNEDREPOSITORY AGE CRITICAL HIGH MEDIUM LOW UNKNOWN CVETOTAL
tanzu-java-web-app Completed 90bd107ad26e228886e2ad28c964580858196376 http://source-controller.flux-system.svc.cluster.local./gitrepository/tap-install/tanzu-java-web-app/90bd107ad26e228886e2ad28c964580858196376.tar.gz 66m

# Image Scan

kubectl get imagescan -n tap-install
NAME PHASE SCANNEDIMAGE AGE CRITICAL HIGH MEDIUM LOW UNKNOWN CVETOTAL
tanzu-java-web-app Completed gcr.io/eknath-se/build-service/tanzu-java-web-app-tap-install@sha256:067cba3141828775b0715f5054f54db33c6b5193c77fc90a31100abcf5f83a71 61m