Documentation for a newer release is available. View Latest
Esta página no está disponible actualmente en Español. Si lo necesita, póngase en contacto con el servicio de asistencia de Icon (correo electrónico)

Deployment

Running as a standalone application

IPF Operational Dashboard can be created with 2 maven profiles:

  • container - Generates docker containers

  • runnable - Generates a runnable jar to run as a standalone application

In this instance we are interested in the runnable jar.

When building IPF Operational Dashboard using the archetype projects both the container and runnable can be generated in the pom.xml.

If you want to generate the runnable you will need to invoke the runnable profile as follows:

mvn clean install -P runnable

All IPF runnable jars are produced as Spring Boot applications. To run an IPF Operational Dashboard, you would execute the following:

java -cp ipf-operational-dashboard-docker/target/com.iconsolutions.payments_ipf-operational-dashboard-docker_3.0.1-SNAPSHOT-runnable.jar:config -Dconfig.override_with_env_vars=true -Dloader.main=com.iconsolutions.ipf.gui.BusinessOperationsMain org.springframework.boot.loader.PropertiesLauncher

If using windows we need to replace the first ":" with a ";", namely: "ods-ingestion-app-1.10.24-runnable.jar;config"

If you are overriding any of the default configuration for the applications then, in this example, the configuration file for the application, application.conf, would live in the config folder as defined by :config. We then leverage the Spring Boot PropertiesLauncher to load the properties into the application.

Additional information

In order to package up the required javacript for the IPF modules you have chosen to serve for your IPF Operational Dashboard you will need these two sections of code in your webapp pom:

<build>
    <resources>
        <resource>
            <directory>${project.build.directory}/static</directory>
            <targetPath>static</targetPath>
        </resource>
    </resources>
</build>
<build>
    <plugins>
        <plugin>
            <groupId>com.github.eirslett</groupId>
            <artifactId>frontend-maven-plugin</artifactId>
            <version>1.12.0</version>
            <configuration>
                <nodeVersion>v20.11.1</nodeVersion>
                <npmVersion>10.2.4</npmVersion>
                <installDirectory>${project.build.directory}</installDirectory>
            </configuration>
            <executions>
                <execution>
                    <id>install node and npm</id>
                    <goals>
                        <goal>install-node-and-npm</goal>
                    </goals>
                </execution>
                <execution>
                    <id>Build</id>
                    <goals>
                        <goal>npm</goal>
                    </goals>
                    <configuration>
                        <arguments>run build</arguments>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

You will need to match this in your service pom with the required java dependencies, ops-gui-service-ng-starter, your webapp, commons-codec and whichever IPF modules you chose:

<dependencies>
    <dependency>
        <groupId>com.iconsolutions.ipf.gui</groupId>
        <artifactId>ops-gui-service-ng-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>com.iconsolutions.payments</groupId>
        <artifactId>iconsolutions</artifactId>
    </dependency>
    <dependency>
        <groupId>com.iconsolutions.ipf.gui</groupId>
        <artifactId>ops-gui-service-ng-cluster</artifactId>
    </dependency>
    <dependency>
        <groupId>com.iconsolutions.ipf.gui</groupId>
        <artifactId>ops-gui-service-ng-htm</artifactId>
    </dependency>
    <dependency>
        <groupId>com.iconsolutions.ipf.gui</groupId>
        <artifactId>ops-gui-service-ng-metrics</artifactId>
    </dependency>
    <dependency>
        <groupId>com.iconsolutions.ipf.gui</groupId>
        <artifactId>ops-gui-service-ng-ods</artifactId>
    </dependency>
    <dependency>
        <groupId>com.iconsolutions.ipf.gui</groupId>
        <artifactId>ops-gui-service-ng-processing-settings</artifactId>
    </dependency>
    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
    </dependency>
</dependencies>

In order to create the required runnable jar.

Building as a Container

All the applications that IPF produce get pushed to ipf-releases in Nexus. The runnable jars will be available as an artifact with a suffix of runnable in the Nexus repository for each version of the application you need. For example if you want to fetch the IPF Operational Dashboard runnable jar for version 3.0.0 you would find it at this link:

The following Dockerfile can be used as a template for integration with your own underlying base container.

FROM registry.ipf.iconsolutions.com/ubi8-minimal-openjdk-11:latest

RUN mkdir -p /ipf-operational-dashboard-service/conf /ipf-operational-dashboard-service/lib

COPY ipf-operational-dashboard-docker-3.0.0-runnable.jar /ipf-operational-dashboard-service/lib/
COPY cinnamon-agent-2.17.3.jar /ipf-operational-dashboard-service/lib/

WORKDIR /ipf-operational-dashboard-service
EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --retries=1 CMD wget -qO- http://localhost:8080/actuator/health/ | grep UP || exit 1

ENTRYPOINT java \
    -javaagent:/ipf-operational-dashboard-service/lib/cinnamon-agent-2.17.3.jar \
    -cp "/ipf-operational-dashboard-service/ipf-operational-dashboard-docker-3.0.0-runnable.jar:/ipf-operational-dashboard-service/conf" \
    $IPF_OPS_GUI_SERVICE_JAVA_ARGS \
    -Dconfig.override_with_env_vars=true \
    -Dloader.main=com.iconsolutions.ipf.gui.BusinessOperationsMain \
    org.springframework.boot.loader.PropertiesLauncher

Kubernetes and OpenShift

The IPF Operational Dashboard operates as an un-clustered, stateless application and so can be easily deployed using the following manifests

Deployment Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
    name: ipf-operational-dashboard
    labels:
        app: operational-dashboard
        product: ipfv2
spec:
    replicas: 3
    selector:
        matchLabels:
            app: operational-dashboard
            product: ipfv2
    template:
        metadata:
            labels:
                app: operational-dashboard
                product: ipfv2
        spec:
            containers:
                - name: operational-dashboard
                  image: CONTAINER_REGISTRY/ipf-operational-dashboard-service:VERSION
                  imagePullPolicy: Always
                  ports:
                    - containerPort: 8080
                      name: server-port
                    - containerPort: 55001
                      name: akka-artery
                  env:
                    - name: IPF_PODNAME
                      valueFrom:
                        fieldRef:
                          fieldPath: metadata.name
                    - name: POD_IP
                      valueFrom:
                        fieldRef:
                          fieldPath: status.podIP
                    - name: POD_NAMESPACE
                      valueFrom:
                        fieldRef:
                          fieldPath: metadata.namespace
                    - name: "AKKA_CLUSTER_BOOTSTRAP_SERVICE_NAME"
                      valueFrom:
                        fieldRef:
                          apiVersion: v1
                          fieldPath: metadata.labels['app']
                    - name: IPF_JAVA_ARGS
                      value: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=60 -XX:InitialRAMPercentage=60 -XX:-PreferContainerQuotaForCPUCount"
                  resources:
                    limits:
                      memory: 3.5Gi
                    requests:
                      memory: 2Gi
                      cpu: 500M
                  livenessProbe:
                    httpGet:
                      path: /actuator/health
                      port: server-port
                      scheme: HTTP
                    initialDelaySeconds: 60
                    periodSeconds: 5
                    timeoutSeconds: 1
                    failureThreshold: 3
                    successThreshold: 1
                  readinessProbe:
                    httpGet:
                      path: /actuator/health
                      port: server-port
                      scheme: HTTP
                    initialDelaySeconds: 60
                    periodSeconds: 5
                    timeoutSeconds: 1
                    failureThreshold: 3
                    successThreshold: 1
                  startupProbe:
                    httpGet:
                      path: /actuator/health
                      port: server-port
                      scheme: HTTP
                    periodSeconds: 10
                    failureThreshold: 30
                  volumeMounts:
                    - name: configuration-dashboard
                      mountPath: /operational-dashboard-service/conf
            volumes:
              - name: configuration-dashboard
                projected:
                  defaultMode: 420
                  sources:
                    - secret:
                      name: operational-dashboard
                      items:
                        - key: users.conf
                          mode: 420
                          path: users.conf
                    - configMap:
                      name: operational-dashboard-cm
                      items:
                        - key: logback.xml
                          mode: 420
                          path: logback.xml
                        - key: application.conf
                          mode: 420
                          path: application.conf
                    - configMap:
                        name: operational-dashboard-cm-summary-layout
                        items:
                          - key: summary-layout.conf
                            mode: 420
                            path: summary-layout.conf
                    - configMap:
                        name: ipf-operational-dashboard-cm-reason-codes
                        items:
                          - key: reason-codes.conf
                            mode: 420
                            path: reason-codes.conf
Service Manifest
apiVersion: v1
kind: Service
metadata:
    name: operational-dashboard
    labels:
        app: operational-dashboard
        product: ipfv2
spec:
    selector:
        app: operational-dashboard
        product: ipfv2
    ports:
        - name: server-port
          protocol: TCP
          port: 8080
          targetPort: 8080
ConfigMap Manifest
apiVersion: v1
kind: ConfigMap
metadata:
    name: ipf-operational-dashboard-cm
data:
    application.conf: |
        spring.data.mongodb.uri = "${ipf.mongodb.url}"
        
        ipf {
          business-operations = {
            auth = {
                jwt {
                  secret = ""
                  roles-claim = "roles"
                }
        
                cors {
                  allowed-origin-patterns = [ "*" ]
                }
        
                saml2 {
                  enabled = true
                  verification-certificate = "classpath:idp.crt"
                  registration-id = "sample-client"
                  single-sign-on-service-location = "https://simplesaml.${environment_name}.ipfdev.co.uk/simplesaml/saml2/idp/SSOService.php"
                  single-log-out-service-location = "https://simplesaml.${environment_name}.example.org/simplesaml/saml2/idp/SingleLogoutService.php"
                  identity-provider-entity-id = "https://simplesaml.${environment_name}.example.org/simplesaml/saml2/idp/metadata.php"
                  service-provider-entity-id = "sample-client"
                  want-authn-requests-signed = false
                  uid-attribute = "uid"
                  roles-attribute = "roles"
                  roles-separator = ","
                  return-url = "/"
                }
        
                oauth2 {
                  enabled = true
                  registrationId = "keycloak"
                  clientId = "login-app"
                  clientSecret = "802e7940-648b-4925-8079-24fa6dc47afe"
                  scopes = "openid, roles"
                  authorizationUri = "https://keycloak.${environment_name}.example.org/realms/demo/protocol/openid-connect/auth"
                  tokenUri = "https://keycloak.${environment_name}.example.org/realms/demo/protocol/openid-connect/token"
                  jwkSetUri = "https://keycloak.${environment_name}.example.org/realms/demo/protocol/openid-connect/certs"
                  returnUrl = "/"
                  rolesFromAttributes = true
                  rolesAttribute = "roles"
                  username = "preferred_username"
                }
            }
            audit = {
                enabled = true
            }
            cluster-management = {
              systems = [
                {
                  name = "Payments Service"
                  base-urls = [
                    "http://payment-service:8558"
                  ],
                  akka-management = true,
                  actuator = {
                    protocol = "http",
                    port = "8080"
                  }
                },
                {
                  name = "Notification Service"
                  base-urls = [
                    "http://notification-service:8558"
                  ],
                  akka-management = true,
                  actuator = {
                    protocol = "http",
                    port = "8080"
                  }
                },
                {
                  name = "ODS Ingestion"
                  base-urls = [
                    "http://ods-ingestion:8558"
                  ],
                  akka-management = true,
                  actuator = {
                    protocol = "http",
                    port = "8080"
                  }
                },
                {
                  name = "ODS Inquiry"
                  base-urls = [
                    "http://ods-inquiry:8080"
                  ],
                  akka-management = false,
                  actuator = {
                    protocol = "http",
                    port = "8080"
                  }
                }
              ]
            }
            metrics = {
              http = {
                client = {
                  host = "grafana"
                  port = "3000"
                  endpoint-url = "/api/health"
                }
              }
              metric-url = "http://grafana:3000",
              local-metric-url = "https://grafana.${environment_name}.example.org",
              call-timeout  = 30s,
              dashboards = [
                {
                  title: "Business Metrics",
                  name: "transactions",
                  id: "0000000001",
                  panels: [
                    {id: "1"},
                    {id: "2"},
                    {id: "15"},
                    {id: "16&var-lookback_period=1y"}
                  ],
                  columns: "2"
                },
                {
                  title: "Debtor Credit Transfer Metrics",
                  name: "transactions",
                  id: "0000000001",
                  panels: [
                    {id: "4&var-behaviour=DebtorCreditTransferBehaviour&var-latency_type=FULL_FLOW", colspan: "1"},
                    {id: "4&var-behaviour=DebtorCreditTransferBehaviour&var-latency_type=CSM_STATES_ONLY", colspan: "1"},
                    {id: "4&var-behaviour=DebtorCreditTransferBehaviour&var-latency_type=NO_CSM_STATES", colspan: "1"},
                    {id: "8&var-behaviour=DebtorCreditTransferBehaviour", colspan: "2"}
                  ],
                  columns: "3"
                },
                {
                  title: "Creditor Credit Transfer Metrics",
                  name: "transactions",
                  id: "0000000001",
                  panels: [
                    {id: "4&var-behaviour=CreditorCreditTransferBehaviour&var-latency_type=FULL_FLOW", colspan: "1"},
                    {id: "4&var-behaviour=CreditorCreditTransferBehaviour&var-latency_type=CSM_STATES_ONLY", colspan: "1"},
                    {id: "4&var-behaviour=CreditorCreditTransferBehaviour&var-latency_type=NO_CSM_STATES", colspan: "1"},
                    {id: "8&var-behaviour=CreditorCreditTransferBehaviour", colspan: "2"}
                  ],
                  columns: "3"
                },
                {
                  title: "Connector Metrics",
                  name: "ipf-connectors",
                  id: "0000000002",
                  panels: [
                    {id: "1"},
                    {id: "2"},
                    {id: "4", colspan: "2"},
                    {id: "5", colspan: "2"}
                  ],
                  columns: "2"
                }
              ]
            }
        
            payment-search = {
                ods = {
                  security = {
                    enabled = false
                    grant_type = "password"
                    client_id = "login-app"
                    client_secret = "802e7940-648b-4925-8079-24fa6dc47afe"
                    username = "test"
                    password = "p4ssw0rd"
                    jwt-certificate = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhXrGmY331co1PX/tDGdMpChoaVfokUMxdrrRul4lLIGSOAEBRegLdmmmY7FgCSTtIhmkkwZWu3gaZLs5+oyld9ncXSL4OpQQvoCOd84RvWiHLhxPBynuYmypTQUP2kLeM3ntCsXI13SwN59tE/y4H/GVGTBDrfN2ELaS43OeZRpQuW1XqLWuyNGDCtC4V7cd+gld5uDBa93PUB40ypWqnYQVrC+PRiiiXcF6uyEgkOgMinR8LerKFi/iFpuMytJa9zW0d/O5aOceulWDjUJeqf+6EbonWjfJv5GMSKdsCjQ6rnq/a1gaxSyYeCctmqtUpu0Ogjjjwfwf3qkj6fWUawIDAQAB"
                  }
                }
                ods-inquiry-url = "http://ods-inquiry:8080"
                payment-summaries.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/views/summaries/payments"
                payment-details.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/views/details"
                system-events.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/catalogue/process-objects/system-events"
                message-logs.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/catalogue/process-objects/message-logs"
                process-flow-events.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/catalogue/process-objects/process-flow-events"
                payment-objects.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/all/payment-objects"
                custom-objects.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/all/custom-objects"
                auth-server.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/realms/demo/protocol/openid-connect/token"
            }
          }
        }

    logback.xml: |
        <?xml version="1.0" encoding="UTF-8"?>
        <configuration>
          <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
            <target>System.out</target>
            <encoder>
              <pattern>[%date{ISO8601}] [%level] [%logger] [%marker] [%thread] - %msg%n</pattern>
            </encoder>
          </appender>
        
          <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
            <queueSize>8192</queueSize>
            <neverBlock>true</neverBlock>
            <appender-ref ref="CONSOLE" />
          </appender>
        
          <logger name="com.iconsolutions" level="ERROR"/>
        
          <root level="INFO">
            <appender-ref ref="ASYNC"/>
          </root>
        
        </configuration>