Docker Image
Parasoft has published official Docker images to Docker Hub for your convenience. There are two versions available, one with Extension Designer and one without. Follow the link below that best suits your needs:
- DTP with Extension Designer: https://hub.docker.com/r/parasoft/dtp-extension-designer
- DTP without Extension Designer: https://hub.docker.com/r/parasoft/dtp
Deploying DTP in Kubernetes with a Helm Chart
Parasoft has published an official Helm chart to Docker Hub for your convenience. Full installation instructions are included in the readme. See https://hub.docker.com/r/parasoft/dtp-helm.
Deploying DTP in Kubernetes Manually
Prerequisites
External Database
Before you start, establish an external database for DTP. You may create a new, empty database or provide an existing one. In either case, a database user must be set up with any necessary permissions required to access the database. Supported databases are MySQL, Oracle, and PostgreSQL (for supported versions, see Requirements and Support). You can use one of the MySQL, Oracle, or PostgreSQL Docker images available on Docker Hub.
Namespace for DTP
Then create a namespace for DTP to run in. For example:
kubectl create namespace parasoft-dtp-namespace
Note: The namespace name "parasoft-dtp-namespace" is used throughout this documentation in command and resource examples. If you use a different name for your namespace, be sure to change any instances of "parasoft-dtp-namespace" in those examples to your namespace name.
Once DTP has been licensed, deleting the namespace will invalidate machine-locked licenses, even if you recreate the same namespace.
Service Account for DTP
Next, you will need a Kubernetes cluster. After starting the cluster, create the service account and permissions required by the DTP pod and related resources. An example of a yaml file that might be used to for this purpose is shown below.
# Stable access for clients to license server apiVersion: v1 kind: ServiceAccount metadata: name: parasoft-account namespace: parasoft-dtp-namespace automountServiceAccountToken: true --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: parasoft-read namespace: parasoft-dtp-namespace rules: - apiGroups: - "" resources: - "namespaces" - "pods" verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: parasoft-read-bind namespace: parasoft-dtp-namespace roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: parasoft-read subjects: - kind: ServiceAccount name: parasoft-account namespace: parasoft-dtp-namespace
Use your yaml file to create the required service account and permissions before creating the DTP environment:
kubectl create -f parasoft-permissions.yaml
You should see something similar to the output below in your console:
serviceaccount/parasoft-account created role.rbac.authorization.k8s.io/parasoft-read created rolebinding.rbac.authorization.k8s.io/parasoft-read-bind created
DTP Setup
Yaml files with kind: Deployment are not supported. Use either kind: Pod or kind: StatefulSet, which are supported. Note that with kind: StatefulSet, only one replica is supported. Be aware that once DTP has been deployed using Pod or StatefulSet, switching the Kind will invalidate machine-locked licenses.
To set up DTP, create a yaml file that defines the following:
- Secret (optional)
- To pull the DTP image from the repository.
- Volume
- To persist data.
- Pod or StatefulSet
- Creates a pod set up to run DTP, Data Collector, and Extension Designer in separate containers.
- Each container is configured with a volume and a liveness probe, which is the Kubernetes equivalent of a Docker Healthcheck.
- Internal-access Service
- Exposes the DTP pod to other pods, allowing them to communicate via the Service name instead of an explicit IP address.
- External-access Service (optional).
- Makes DTP, Data Collector, and Extension Designer accessible via external clients by allocating ports in the node and mapping them to ports in the pod.
Example yaml files for a Pod or a StatefulSet (both called "parasoft-dtp.yaml") are shown below. These examples use an NFS volume, but this is not required; use whatever volume type fits your needs.
Required Settings for a Stable Machine ID
As you modify either of the dtp-pod.yaml samples shown below or craft your own yaml, be aware that the following fields need to be consistent across upgrades and redeployments in order to assure a stable machine ID:
- metadata: name
- metadata: namespace
- containers: name
In addition, the following environment variables are required:
- env: name: PARASOFT_POD_NAME
- env: name: PARASOFT_POD_NAMESPACE
Example yaml using 'kind: Pod'
If you are using a secret for OIDC configuration, also see the Working with OIDC Secret section below.
apiVersion: v1
kind: Pod
metadata:
name: dtp
namespace: parasoft-dtp-namespace
labels:
app: DTP
spec:
volumes:
- name: dtp-data
nfs:
server: NFS_SERVER_HOST
path: /dtp/
# Uncomment section below if you are setting up a custom keystore; you will also need to uncomment out the associated volumeMounts below
# - name: keystore-cfgmap-volume
# configMap:
# name: keystore-cfgmap
securityContext:
runAsNonRoot: true
containers:
- name: dtp
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
image: DTP_DOCKER_IMAGE
imagePullPolicy: Always
env:
- name: PARASOFT_POD_NAME #REQUIRED, DO NOT CHANGE
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: PARASOFT_POD_NAMESPACE #REQUIRED, DO NOT CHANGE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
# To configure DTP to connect to your database on startup, please provide your database type, user, user password, and connection URL to the container environment by injecting the values as the DB_TYPE, DB_USER, DB_PASSWORD, and DB_URL environment variables.
# To prevent exposing sensitive data, please create a user password secret to use for the DB_PASSWORD environment variable.
# Note that the database type must be one of the following: mysql | oracle | postgresql
# Note that the environment variable values will override the equivalent persisted values in the PSTRootConfig.xml each time the container or pod is restarted.
# The following are example settings for a MySQL container called "mysql-container", a database called "DTP", a user called "dtp_user", and a user password secret.
# - name: DB_TYPE
# value: "mysql"
# - name: DB_USER
# value: "dtp_user"
# - name: DB_PASSWORD
# valueFrom:
# secretKeyRef:
# name: YOUR_DB_USER_PASSWORD_SECRET
# key: YOUR_DB_USER_PASSWORD_SECRET_KEY
# - name: DB_URL
# value: "jdbc:mysql://mysql-container:3306/DTP"
# To configure DTP to automatically download the driver for your database on startup, please provide the JDBC driver URL to the container environment by injecting the value as the JDBC_DRIVER_URL environment variable.
# The following is an example URL to download the JDBC driver for MySQL 8.0.30.
# - name: JDBC_DRIVER_URL
# value: "https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.30/mysql-connector-java-8.0.30.jar"
# Another option is to download the JDBC driver manually one time.
# See section titled Database and JDBC Client Jar on https://hub.docker.com/r/parasoft/dtp or https://hub.docker.com/r/parasoft/dtp-extension-designer.
# To configure DTP to use JVM arguments, please provide the arguments to the container environment by injecting the value as the JAVA_CONFIG_ARGS environment variable.
# The following is an example JVM argument "com.parasoft.sdm.storage.managers.admin.enable.delete.project.data=true"
# - name: JAVA_CONFIG_ARGS
# value: "-Dcom.parasoft.sdm.storage.managers.admin.enable.delete.project.data=true"
args: ["--run", "dtp"]
ports:
- name: "dtp-http-port"
containerPort: 8080
- name: "dtp-https-port"
containerPort: 8443
volumeMounts:
- mountPath: "/usr/local/parasoft/data"
name: dtp-data
# Uncomment section below if you are setting up a custom keystore. Note that updates made to these files will not be reflected inside the container once it's been deployed; you will need to restart the container for it to contain any updates.
# - name: keystore-cfgmap-volume
# mountPath: "/usr/local/parasoft/dtp/tomcat/conf/.keystore"
# subPath: keystore
# - name: keystore-cfgmap-volume
# mountPath: "/usr/local/parasoft/dtp/tomcat/conf/server.xml"
# subPath: server-config
# To prevent liveness probe failures on environments with low or overly taxed RAM/CPU, we recommend increasing the timeout seconds
livenessProbe:
exec:
command:
- healthcheck.sh
- --verify
- dtp
initialDelaySeconds: 120
periodSeconds: 60
timeoutSeconds: 30
failureThreshold: 5
- name: data-collector
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
image: DTP_DOCKER_IMAGE
imagePullPolicy: Always
# To inject JVM arguments into the container, specify the "env" property as in the example below, which injects JAVA_DC_CONFIG_ARGS
# env:
# - name: JAVA_DC_CONFIG_ARGS
# value: "-Dcom.parasoft.sdm.dc.traffic.max.length=1000000"
args: ["--run", "datacollector", "--no-copy-data"]
ports:
- name: "dc-port"
containerPort: 8082
volumeMounts:
- mountPath: "/usr/local/parasoft/data"
name: dtp-data
# To prevent liveness probe failures on environments with low or overly taxed RAM/CPU, we recommend increasing the timeout seconds
livenessProbe:
exec:
command:
- healthcheck.sh
- --verify
- datacollector
initialDelaySeconds: 120
periodSeconds: 60
timeoutSeconds: 30
failureThreshold: 5
# Uncomment section below if using DTP with Extension Designer
# - name: extension-designer
# securityContext:
# allowPrivilegeEscalation: false
# capabilities:
# drop: ["ALL"]
# seccompProfile:
# type: RuntimeDefault
# image: DTP_DOCKER_IMAGE
# imagePullPolicy: Always
# To inject JVM arguments into the container, specify the "env" property as in the example below, which injects JAVA_CONFIG_ARGS
# args: ["--run", "dtpservices"]
# ports:
# - name: "dep-port"
# containerPort: 8314
# volumeMounts:
# - mountPath: "/usr/local/parasoft/data"
# name: dtp-data
# To prevent liveness probe failures on environments with low or overly taxed RAM/CPU, we recommend increasing the timeout seconds
# livenessProbe:
# exec:
# command:
# - healthcheck.sh
# - --verify
# - dtpservices
# initialDelaySeconds: 120
# periodSeconds: 60
# timeoutSeconds: 30
# failureThreshold: 5
# Uncomment section below if using Extension Designer with an external MongoDB
# env:
# - name: DEP_USE_REMOTE_DB
# value: "true"
# - name: DEP_DB_HOSTNAME
# value: "mongodb-hostname" # Put your mongodb hostname here
# - name: DEP_DB_PORT
# value: "27017"
restartPolicy: Always
serviceAccountName: parasoft-account
automountServiceAccountToken: true
imagePullSecrets:
- name: YOUR_SECRET
Example yaml using 'kind: StatefulSet'
If you are using a secret for OIDC configuration, also see the Working with OIDC Secret section below.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: dtp
namespace: parasoft-dtp-namespace
labels:
app: DTP
spec:
selector:
matchLabels:
app: DTP
serviceName: dtp-service
replicas: 1 #Only 1 is supported
template:
metadata:
labels:
app: DTP
spec:
volumes:
- name: dtp-data
nfs:
server: NFS_SERVER_HOST
path: /dtp/
# persistentVolumeClaim:
# claimName: dtp-pvc
# Uncomment section below if you are setting up a custom keystore; you will also need to uncomment out the associated volumeMounts below
# - name: keystore-cfgmap-volume
# configMap:
# name: keystore-cfgmap
securityContext:
runAsNonRoot: true
containers:
- name: dtp
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
image: DTP_DOCKER_IMAGE
imagePullPolicy: Always
env:
- name: PARASOFT_POD_NAME #REQUIRED, DO NOT CHANGE
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: PARASOFT_POD_NAMESPACE #REQUIRED, DO NOT CHANGE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
# To configure DTP to connect to your database on startup, please provide your database type, user, user password, and connection URL to the container environment by injecting the values as the DB_TYPE, DB_USER, DB_PASSWORD, and DB_URL environment variables.
# To prevent exposing sensitive data, please create a user password secret to use for the DB_PASSWORD environment variable.
# Note that the database type must be one of the following: mysql | oracle | postgresql
# Note that the environment variable values will override the equivalent persisted values in the PSTRootConfig.xml each time the container or pod is restarted.
# The following are example settings for a MySQL container called "mysql-container", a database called "DTP", a user called "dtp_user", and a user password secret.
# - name: DB_TYPE
# value: "mysql"
# - name: DB_USER
# value: "dtp_user"
# - name: DB_PASSWORD
# valueFrom:
# secretKeyRef:
# name: YOUR_DB_USER_PASSWORD_SECRET
# key: YOUR_DB_USER_PASSWORD_SECRET_KEY
# - name: DB_URL
# value: "jdbc:mysql://mysql-container:3306/DTP"
# To configure DTP to automatically download the driver for your database on startup, please provide the JDBC driver URL to the container environment by injecting the value as the JDBC_DRIVER_URL environment variable.
# The following is an example URL to download the JDBC driver for MySQL 8.0.30.
# - name: JDBC_DRIVER_URL
# value: "https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.30/mysql-connector-java-8.0.30.jar"
# Another option is to download the JDBC driver manually one time.
# See section titled Database and JDBC Client Jar on https://hub.docker.com/r/parasoft/dtp or https://hub.docker.com/r/parasoft/dtp-extension-designer.
# To configure DTP to use JVM arguments, please provide the arguments to the container environment by injecting the value as the JAVA_CONFIG_ARGS environment variable.
# The following is an example JVM argument "com.parasoft.sdm.storage.managers.admin.enable.delete.project.data=true"
# - name: JAVA_CONFIG_ARGS
# value: "-Dcom.parasoft.sdm.storage.managers.admin.enable.delete.project.data=true"
args: ["--run", "dtp"]
ports:
- name: "dtp-http-port"
containerPort: 8080
- name: "dtp-https-port"
containerPort: 8443
volumeMounts:
- mountPath: "/usr/local/parasoft/data"
name: dtp-data
# Uncomment section below if you are setting up a custom keystore. Note that updates made to these files will not be reflected inside the container once it's been deployed; you will need to restart the container for it to contain any updates.
# - name: keystore-cfgmap-volume
# mountPath: "/usr/local/parasoft/dtp/tomcat/conf/.keystore"
# subPath: keystore
# - name: keystore-cfgmap-volume
# mountPath: "/usr/local/parasoft/dtp/tomcat/conf/server.xml"
# subPath: server-config
# To prevent liveness probe failures on environments with low or overly taxed RAM/CPU, we recommend increasing the timeout seconds
livenessProbe:
exec:
command:
- healthcheck.sh
- --verify
- dtp
initialDelaySeconds: 120
periodSeconds: 60
timeoutSeconds: 30
failureThreshold: 5
- name: data-collector
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
image: DTP_DOCKER_IMAGE
imagePullPolicy: Always
# To inject JVM arguments into the container, specify the "env" property as in the example below, which injects JAVA_DC_CONFIG_ARGS
# env:
# - name: JAVA_DC_CONFIG_ARGS
# value: "-Dcom.parasoft.sdm.dc.traffic.max.length=1000000"
args: ["--run", "datacollector", "--no-copy-data"]
ports:
- name: "dc-port"
containerPort: 8082
volumeMounts:
- mountPath: "/usr/local/parasoft/data"
name: dtp-data
# To prevent liveness probe failures on environments with low or overly taxed RAM/CPU, we recommend increasing the timeout seconds
livenessProbe:
exec:
command:
- healthcheck.sh
- --verify
- datacollector
initialDelaySeconds: 120
periodSeconds: 60
timeoutSeconds: 30
failureThreshold: 5
# Uncomment section below if using DTP with Extension Designer
# - name: extension-designer
# securityContext:
# allowPrivilegeEscalation: false
# capabilities:
# drop: ["ALL"]
# seccompProfile:
# type: RuntimeDefault
# image: DTP_DOCKER_IMAGE
# imagePullPolicy: Always
# To inject JVM arguments into the container, specify the "env" property as in the example below, which injects JAVA_CONFIG_ARGS
# args: ["--run", "dtpservices"]
# ports:
# - name: "dep-port"
# containerPort: 8314
# volumeMounts:
# - mountPath: "/usr/local/parasoft/data"
# name: dtp-data
# To prevent liveness probe failures on environments with low or overly taxed RAM/CPU, we recommend increasing the timeout seconds
# livenessProbe:
# exec:
# command:
# - healthcheck.sh
# - --verify
# - dtpservices
# initialDelaySeconds: 120
# periodSeconds: 60
# timeoutSeconds: 30
# failureThreshold: 5
# Uncomment section below if using Extension Designer with an external MongoDB
# env:
# - name: DEP_USE_REMOTE_DB
# value: "true"
# - name: DEP_DB_HOSTNAME
# value: "mongodb-hostname" # Put your mongodb hostname here
# - name: DEP_DB_PORT
# value: "27017"
restartPolicy: Always
serviceAccountName: parasoft-account
automountServiceAccountToken: true
imagePullSecrets:
- name: YOUR_SECRET
Service Definition
Create the service that can be used to access the DTP server in Kubernetes. The example shown below exposes it using a node port, which provides a stable endpoint for applications to access it.
apiVersion: v1
kind: Service
metadata:
name: dtp-svc
namespace: parasoft-dtp-namespace
spec:
type: NodePort
selector:
app: DTP
ports:
- name: "dtp-http-port"
protocol: TCP
port: 8080
nodePort: 30080
- name: "dc-port"
protocol: TCP
port: 8082
nodePort: 30082
- name: "dtp-https-port"
protocol: TCP
port: 8443
nodePort: 30443
# Uncomment section below if using DTP with Extension Designer
# - name: "dep-port"
# protocol: TCP
# port: 8314
# nodePort: 30314
# SERVICE CONFIG NOTES:
# 'name' can be whatever you want, but must follow the DNS label naming conventions
# 'nodePort' must be between 30000-32768
# 'spec.selector' must match 'metadata.labels' in pod config
Create the DTP Environment
DTP startup can take up to two minutes, assuming that minimum hardware requirements are met. During the startup time, DTP will not be accessible from the browser.
Prepare the volume mount location on your cluster. By default, the image runs as the "parasoft" user with a UID of 1000 and GID of 1000. Prepare the volume such that this user has read and write access to it.
Then create the DTP environment defined in the DTP setup yaml file created previously:
kubectl create -f parasoft-dtp.yaml
This will initialize the contents of the persistent volume.
If you injected any environment variables such as JVM arguments or database configuration settings, you can use the following command to verify their status:
kubectl exec <POD_NAME> -c <CONTAINER_NAME> -- printenv
Accessing DTP, Data Collector, and Extension Designer Externally
To allow external access to DTP, Data Collector, and/or Extension Designer in your cluster, use Ingress, an OpenShift route, or an equivalent resource set up to function as a reverse proxy as described in Reverse Proxy Support. If you are using DTP with Extension Designer, you will need to update the Reverse Proxy settings in Extension Designer to reflect the expected hostname and the exposed ports for accessing DTP and Extension Designer.
Configuring the Database JDBC URL
The structure of your database URL depends on how your external database is accessed. For example:
- Database is hosted on a remote server. In this case (assuming a MySQL database), the URL might look like this:
jdbc:mysql://<DB_HOST>:3306/DTP. - Database is running within your K8 cluster and accessible via configured service name or IP address. In this case, (assuming a MySQL database), the URL might look like this:
jdbc:mysql://<CONFIGURED-SERVICE-NAME>.<NAMESPACE>:3306/DTP.
Custom Keystore
If you want to set up a custom keystore, you will need to create a configuration map for the .keystore and server.xml files. The command below creates a configuration map called "keystore-cfgmap" with file mappings for the custom .keystore and server.xml files. In this example, each file mapping is given a key: "keystore" for the .keystore file and "server-config" for the server.xml file. While giving each file mapping a key is not necessary, it is useful when you don't want the key to be the file name.
~$ kubectl create configmap keystore-cfgmap --from-file=keystore=/path/to/.keystore --from-file=server-config=/path/to/server.xml configmap/keystore-cfgmap created
Note: The custom server.xml being used in a configuration map may need to be updated manually when upgrading to a newer DTP Docker image. Any required changes for a given version will be documented in its release notes. Be sure to check all the release notes between your current version and the one to which you are upgrading.
Custom Truststore
Using a custom truststore in Kubernetes environments is similar to using a custom keystore as described above. Adjust the directions for using a custom keystore as appropriate. Note that the truststore location is /usr/local/parasoft/dtp/jre/lib/security/cacerts.
Volume Mount Security Policies (Optional)
If your security policy requires applications to only write to mounted volumes, then in addition to the dtp-data directory (which is already set in the example pod) you will need to mount the following locations for the specified containers:
dtp-server:
/usr/local/parasoft/dtp/tomcat/temp /usr/local/parasoft/dtp/tomcat/logs /usr/local/parasoft/dtp/tomcat/work /usr/local/parasoft/dtp/tomcat/conf/PST /usr/local/parasoft/dtp/tomcat/conf/Internal
extension-designer (if enabled):
/tmp
Notes:
- Do not mount
/usr/local/parasoft/data-tmp. You can ignore errors related todata-tmplike "rm: cannot remove '/usr/local/parasoft/data-tmp/LicenseServer/conf/licserv.enc': Read-only file system" in the Kubernetes logs. - You can ignore errors related to server.xml if the file is not mapped using a configuration map as documented in Custom Keystore.
Working with OIDC Secret (Optional)
You can configure an OIDC secret for DTP. To do so:
- Create a secret for your OIDC configuration.
Example OIDC secret yamldtp-oidc-secret.yamlapiVersion: v1 kind: Secret metadata: name: dtp-oidc-secret namespace: parasoft-dtp-namespace type: Opaque stringData: oidc_json: | { "enabled": true, "issuerUri": "your issuer uri", "clientId": "your client id", "clientSecret": "your client secret", "scopes": ["openid", "profile", "email"], "claimMappings": { "username": "preferred_username", "firstName": "given_name", "lastName": "family_name", "email": "email" }, "adminUsers": ["your admin user"] } - Update your dtp-pod.yaml as follows:
- Add an OIDC secret volume under
spec.volumes:# Adding the OIDC secret as a volume - name: dtp-oidc-secret-volume secret: secretName: dtp-oidc-secret optional: true items: - key: oidc_json path: oidc.json - Add an
initContainerssection that will populate the data directory before the other containers are started underspecand beforecontainers. This should use the DTP image and the DTP data directory/usr/local/parasoft/data:# Populating the data directory before main container starts initContainers: - name: init-dtp-data image: DTP_DOCKER_IMAGE command: ["/bin/sh", "-c"] args: - | set -e if [ ! -f "$DTP_DATADIR/.dtprc" ]; then touch "$DTP_DATADIR/.dtprc" cp --preserve=timestamps -rnT "$TMP_DATADIR" "$DTP_DATADIR" fi volumeMounts: - mountPath: "/usr/local/parasoft/data" name: dtp-data - Add an OIDC secret volume under
spec.containers.name (=dtp and =data-collector).volumeMounts:# Mounting the OIDC secret volume to the container - name: dtp-oidc-secret-volume mountPath: "/usr/local/parasoft/data/conf/oidc.json" subPath: oidc.json readOnly: true
- Add an OIDC secret volume under
Troubleshooting
Accessing Additional Logging
DTP captures debugging logs automatically. You can access them in the tests.log file found in the <DTP_DATA_DIR>/logs/ directory.
machineId is LINUX2-0
This issue can occur when there is an underlying permission issue. To resolve it, try the following options:
- Search the tests.log file found in the
<DTP_DATA_DIR>/logs/directory for the error: "Kubernetes API call fails with status=403 error". - Verify that you have created permissions required by DTP using parasoft-permissions.yaml.
- Note: if you are upgrading, make sure to use the parasoft-permissions.yaml for the version to which you are upgrading.
- Confirm that all Parasoft-required resources are using the same namespace.
machineId changes when pod restarts
- This issue can occur if you are using an unsupported Kubernetes object. Make sure you are not using kind: Deployment. Only kind: Pod and kind: StatefulSet are supported.
- Make sure you are following all the directions under Required Settings for a Stable Machine ID.
- Make sure that your environment is not setting
automountServiceAccountTokentofalse.