Developing an App for Great Bear

This tutorial is the first part of a four part series. The series is about creating a basic application for Great Bear, extending it with a liveness probe, metrics and finally publishing it.

Other parts of the series:

This guide gives an example of how to develop an application for Great Bear. The guide assumes that you are already familiar with using docker and creating docker images, creating Helm charts for Kubernetes, and deploying applications to Kubernetes clusters using Helm charts. The guide also assumes that you have docker and helm installed, and the required access credentials for Great Bear. For more information on the development process for Great Bear, see Great Bear Application Lifecycle.

You are going to create a very basic application called ‘Bear Echo’, which is one of the simplest apps you can deploy with Great Bear, and can be useful for testing purposes. Bear Echo will use python Flask to serve a simple webpage, and allow users to define a custom text message when deploying the app that is then used as header in the rendered web page.

Create the application and Dockerfile

  1. First step is to create a new directory called bear-echo where you will save the code for the application.

  2. Next we need to write the python code for the Bear Echo server, create a file in the bear-echo directory called, copy the following code and save the file:

    # bear-echo/
    import os
    import sys
    import importlib
    from flask import Flask
    _HTTP_PORT: int = int(os.getenv('HTTP_PORT') or '11777')
    _BASE_URL: str = os.getenv('BASE_URL') or '/'
    _ECHO_TEXT: str = os.getenv('ECHO_TEXT') or 'Hello World'
    app = Flask(__name__)
    @app.route(_BASE_URL, methods=['GET'])
    def echo():
    def _main():
        print('ECHO_TEXT: ' + _ECHO_TEXT)
        print(f'Started on{_HTTP_PORT}{_BASE_URL}')'', port=_HTTP_PORT)
    if __name__ == '__main__':
  3. Now we have our python application, we need a Dockerfile to create the image that will be uploaded to the Great Bear Application Store,. Create a file in the bear-echo directory called Dockerfile, copy the following code and save the file:

    # bear-echo/Dockerfile
    FROM python:3.8-alpine
    WORKDIR /echo
    RUN pip install flask
    COPY ./
    EXPOSE 11777
    CMD [ "python", "" ]

Create the Great Bear Application Package (GBAP)

  1. With the Dockerfile created, you can now move onto creating the GBAP, used to publish our application to the Great Bear Application Store for subsequent deployment to Great Bear managed sites through the Great Bear Dashboard.
  2. Follow the steps to install and configure the Great Bear Packaging SDK tool.
  3. Use the SDK tool to create a new GBAP instance within your bear-echo directory, entering the desired fields as prompted:
    $ gbear app create bear-echo/GBAP
    Creating a GBAP with required fields....
    Enter the chart name:
    Enter the chart version [SemVer2 MAJOR.MINOR.PATCH], (leave empty for default: 0.0.1):
    Enter the application name:
    Enter the application description:
    A simple HTTP echo service, based on Python Flask.
    Successfully created a GBAP in path bear-echo/GBAP
  4. The SDK tool has now created a boiler plate GBAP filesystem tree:
    ├── Chart.yaml
    ├── gbear
    │   └── appmetadata.yaml

Author the GBAP Assets

The GBAP contains the following two fundamental elements:

  1. A Helm Chart to describe a set of Kubernetes resources used to deploy the application at Great Bear managed sites.
  2. Great Bear specific application metadata (gbear/appmetadata), containing:
    • Properties used to render the application in the Great Bear Application Store
    • Properties used to define application parameters which can be overridden when deploying the Helm Chart.

For further details on the GBAP structure, see Great Bear Application Package Guide

Create the Helm values file

  1. Inside the GBAP directory, we next need to create a file called values.yaml.

    ├── Chart.yaml
    ├── gbear
    │   └── appmetadata.yaml
    ├── values.yaml
  2. This file will contain the default values for our Bear Echo application, and includes read-only parameters like GB_SITE_NAME and GB_NODE_NAMES which are provided by Great Bear as environment variables when it deploys the application. It also defines the image repository location, and you should make sure to replace <tenant-registry-url> in the repository value to the tenant registry URL communicated to you by Cisco.


    replicaCount: 1
    strategyType: Recreate
      repository: <tenant-registry-url>/bear-echo
      tag: 0.0.1
      pullPolicy: IfNotPresent
    - name: gbear-harbor-pull
      # Config parameters described in application metadata
      # Their values will be injected here by the Great Bear deployer
      GB_SITE_NAME: |
            Site name with "quotes" and 'quotes'
      GB_NODE_NAMES: |
            {"node1":"Display name 1 with \"quotes\" and 'quotes'","node2":"Display name 2 with \"quotes\" and 'quotes'"}
      ACCESS_PORT: |
      type: NodePort
      port: 11777
        port: 11777
        protocol: TCP
        nodePort: "{{ default 31777 (int .Values.config.ACCESS_PORT)}}"
    # ----
    nameOverride: ""
    fullnameOverride: ""
    nodeSelector: {}
    affinity: {}
        enabled: false
        istioGateway: ""
        servicePath: ""
    siteId: ""

Create the Helm template files

  1. The next step is to create the template files that will create our Kubernetes deployment and the service endpoint for the deployment, along with some template helpers that are re-used throughout the chart. Therefore, inside the GBAP directory, create a new directory called templates

    ├── Chart.yaml
    ├── gbear
    │   └── appmetadata.yaml
    ├── templates
  2. The first file we will create in the new templates directory is _helpers.tpl, which will hold some template helpers that we can re-use throughout the chart. Your bear-echo/GBAP/templates/_helpers.tpl file should contain the following helpers:

    Expand the name of the chart.
    {{- define "" -}}
    {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
    {{- end -}}
    Create a default fully qualified app name.
    We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
    If release name contains chart name it will be used as a full name.
    {{- define "app.fullname" -}}
    {{- if .Values.fullnameOverride -}}
    {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
    {{- else -}}
    {{- $name := default .Chart.Name .Values.nameOverride -}}
    {{- if contains $name .Release.Name -}}
    {{- .Release.Name | trunc 63 | trimSuffix "-" -}}
    {{- else -}}
    {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
    {{- end -}}
    {{- end -}}
    {{- end -}}
    Create chart name and version as used by the chart label.
    {{- define "app.chart" -}}
    {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
    {{- end -}}
    Common labels
    {{- define "app.labels" -}} {{ .Chart.Name }} {{ .Release.Name }} {{ .Release.Service }} {{ include "app.chart" . }}
    {{- end -}}
    Selector labels
    {{- define "app.selector" -}} {{ .Chart.Name }} {{ .Release.Name }}
    {{- end -}}
  3. The next file to create in the templates directory is the deployment.yaml file, which will provide a basic manifest for creating the Kubernetes deployment our Bear Echo application. In our case, the bear-echo/GBAP/templates/deployment.yaml should contain:

    apiVersion: apps/v1
    kind: Deployment
      name: {{ include "app.fullname" . }}
      labels: {{- include "app.labels" . | nindent 4 }}
      replicas: {{ .Values.replicaCount }}
        type: {{ .Values.strategyType }}
        matchLabels: {{- include "app.selector" . | nindent 6 }}
          labels: {{- include "app.selector" . | nindent 8 }}
        {{- with .Values.imagePullSecrets }}
            {{ toYaml . | indent 8 | trim }}
        {{- end }}
        {{- with .Values.podSecurityContext }}
            {{ toYaml . | indent 8 | trim }}
        {{- end }}
            - name: {{ .Chart.Name }}
              image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
              imagePullPolicy: {{ .Values.image.pullPolicy }}
            {{- with .Values.config }}
              {{- range $key, $value := . }}
              - name: {{$key}}
                # Some config parameters (GB_SITE_NAME, GB_NODE_NAMES)
                # can contain both quotes "" and '', for example: {"node1": "User's home"}
                # and the value itself should be quoted in order not to be parsed as object
                # so it's required to put it as a text field to make both quotes processable
                value: |
                                {{$value -}}
              {{- end }}
            {{- end }}
            {{- with .Values.ports }}
              {{- range $key, $value := . }}
                - name: {{ $key }}
                  containerPort: {{ $value.port }}
                  protocol: {{ default "TCP" $value.protocol }}
              {{- end }}
            {{- end }}
            {{- with .Values.securityContext }}
                {{ toYaml . | indent 12 | trim }}
            {{- end }}
            {{- with .Values.resources }}
                {{ toYaml . | indent 12 | trim }}
            {{- end }}
        {{- with .Values.nodeSelector }}
            {{ toYaml . | indent 8 | trim }}
        {{- end }}
        {{- with .Values.affinity }}
            {{ toYaml . | indent 8 | trim }}
        {{- end }}
        {{- with .Values.tolerations }}
            {{ toYaml . | indent 8 | trim }}
        {{- end }}
  4. The final file that is needed to complete the templates directory is the service.yaml file, which provides a basic manifest for creating a service endpoint the Bear Echo deployment. The bear-echo/GBAP/templates/service.yaml should contain this code:

    apiVersion: v1
    kind: Service
      name: {{ include "app.fullname" . }}
      labels: {{- include "app.labels" . | nindent 4 }}
    {{- with .Values.service.annotations }}
      {{- range $key, $value := . }}
        {{ $key }}: {{ $value | quote }}
      {{- end }}
    {{- end }}
      type: {{ .Values.service.type }}
    {{- with .Values.ports }}
      {{- range $key, $value := . }}
        - name: {{ $key }}
          port: {{ $value.port }}
          nodePort: {{ tpl $value.nodePort $ }}
          protocol: {{ default "TCP" $value.protocol }}
      {{- end }}
    {{- end }}
      selector: {{- include "app.selector" . | nindent 4 }}

Customise the Great Bear Application Metadata

  1. The SDK tool has generated the following boiler plate gbear/appmetadata.yaml file, containing our application name and description entered when we created the GBAP.


    displayName: bear-echo
    description: A simple HTTP echo service, based on Python Flask.
    # The following optional fields provide a more detailed properties to describe your GBAP.
    # Uncomment as required and modify field values specifically for your application.
    # Specify whether the application should be deployed once per great bear site (defaults to false if not explicitly defined)
    #singleton: true
    # A dictionary (key-values) to label applications to handle specially like kind: EXAMPLE These should be explicit attributes.
    #   kind: EXAMPLE
    # A list of tags used by the Great Bear application store to present applications such as e.g the private attribute.
    #   - example-apps
    #   - blog
    # A list of architectures supported like [amd64, arm64v8]
    #   - amd64
    # A path/URL of icon to use in the app store
    # A list of configuration fields that are provided as run-time values of the Helm deployment.
    #   - name: outputMessage                       # The name of the field.
    #     key: application.message                  # A dot-separated name where the value of this field will be added to the Helm values. Like postgres.image.tag. Defaults to config.{name} (current behavior).
    #     title: Output message                     # The label of the parameter to be displayed on the Great Bear dashboard when deploying the application.
    #     description: Message sent to the user     # The description of the parameter to be displayed on the Great Bear dashboard when deploying the application.
    #     value: Hello World                        # The default value of the parameter
    #     type: String                              # A simple string, rendered as a textbox
    #   - name: password
    #     key: application.password
    #     title: Initial password
    #     type: Secret                              # A string, rendered as a password input.
    #   - nodeport
    #     key: service.nodePorts.https
    #     type: NodePort                            # A port number in the 30000-32767 range.
    #     title: Node port to serve HTTPS on
    #     value: 37777
    #   - name: sitename
    #     key: application.mySite
    #     type: Runtime                             # An injected variable via Go Templating performed on the value of this field. Field is not shown on the UI.
    #     value: This application is running in {{.GB_SITE_ID}}  # This value will be substituted by GB when the application is deployed to the edge
    #   - name: forceLoginToggle
    #     key: application.forceLogin
    #     choices: [yes, no]                         # UI renders the parameter as a toggle switch, the UI will recognise the [yes, no] options as a toggle switch.
    #     value: yes                                 # Dashboard will display a toggle switch in the enabled position
    #   - name: applicationTextColor
    #     key: application.textColor
    #     choices: [black, red, blue]                # UI renders the parameter as a dropdown list, containing the specified values.
    #     value: red                                 # Dashboard will display a dropdown list with specified value 'red' as the default
  2. The next step is to specialise this boiler plate file with specific application metadata for our bear-echo application, editing properties to determine its visibility in the Application Store, and specify which parameters a user can set when the application is deployed. Edit your bear-echo/GBAP/gbear/appmetadata.yaml file to include the following properties, which enables ECHO_TEXT and ACCESS_PORT to be defined by a user deploying the application through Great Bear, and other important details:

    displayName: bear-echo
    description: A simple HTTP echo service, based on Python Flask.
    - debug
    - sample
    # Defines whether the app can be deployed to the same site several times
    singleton: true    
      - name: "ECHO_TEXT"
        title: "Message to say"
        description: "The application will echo this text"
        value: "A Hello from Great Bear."            # Default used if not overridden by the user
        type: String
      - name: "ACCESS_PORT"
        title: "Access HTTP port (30000-32767)"
        value: 31777                               # Default used if not overridden by the user
        type: NodePort

Validate the GBAP

  1. Now that we have finished authoring our GBAP assets, we can use the SDK tool to validate the contents:
$ gbear app validate bear-echo/GBAP
Validating Package: charts/
-- Validating Application Metadata...
-- Validating Chart Dependencies...
-- Validating Kubernetes Resource Labels...
Validation Complete:
Application Metadata: Passed (Errors: 0, Warnings: 0)
Chart Dependencies: Passed (Errors: 0, Warnings: 0)
Kubernetes Resource Labels: Passed (Errors: 0, Warnings: 0)

For further details on GBAP validation, see Validate an Application Package

  1. As the GBAP contains a Helm Chart, it is good practice to test that the Chart can be successfully installed to a local Kubernetes cluster using
    helm install bear-echo charts/ --dry-run --debug

Next steps

Assuming the everything is setup correctly, we can now move on to extend the application, see: