How to add/replace the module in Vagrant box (Docker image from Docker Hub)

Step-by-step guide

This instruction is based on modifications made for mod-notes module and pre-built testing-backend Vagrant box

 Phase: Module preparations
  1. Update the raml file

     sample raml file
    #%RAML 0.8
    
    title: Notes API
    baseUri: https://github.com/folio-org/mod-notes
    version: v1
    
    documentation:
      - title: mod-notes API
        content: This documents the API calls that can be made to query and manage notes about all kind of objects
    
    schemas:
      - note.json: !include note.json
      - noteCollection: !include noteCollection.json
      - errors: !include raml-util/schemas/errors.schema
      - error.schema: !include raml-util/schemas/error.schema
      - parameters.schema: !include raml-util/schemas/parameters.schema
      - raml-util/schemas/metadata.schema: !include raml-util/schemas/metadata.schema
    
    traits:
      - validate: !include raml-util/traits/validation.raml
      - secured: !include raml-util/traits/auth.raml
      - language: !include raml-util/traits/language.raml
      - pageable:  !include raml-util/traits/pageable.raml
      - searchable: !include raml-util/traits/searchable.raml
    
    resourceTypes:
      - collection: !include raml-util/rtypes/collection.raml
      - collection-item: !include raml-util/rtypes/item-collection.raml
    
    /notes:
      displayName: Notes
      description: Notes collection
      type:
        collection:
          schemaCollection: noteCollection
          schemaItem: note.json
          exampleCollection: !include examples/noteCollection.sample
          exampleItem: !include examples/note.sample
    
      post:
        is: [ validate ]
      get:
        is: [
          searchable: {description: "with valid searchable fields: for example link = 1234", example: "link=/users/1234"},
          pageable,
          validate
        ]
        responses:
      /_self:
        displayName: Notes created by current user
        type:
          collection:
            schemaCollection: noteCollection
            schemaItem: note.json
            exampleCollection: !include examples/noteCollection.sample
            exampleItem: !include examples/note.sample
        get:
          is: [
            searchable: {description: "with valid searchable fields: for example link = 1234", example: "link=/users/1234"},
            pageable,
            validate
          ]
      /{id}:
        displayName: Notes
        description: Get, Delete or Update a specific note
        type:
          collection-item:
            schema: note.json
            exampleItem: !include examples/note.sample
        get:
          is: [ validate ]
          responses:
            400:
              description: "Bad request"
              body:
                text/plain:
            401:
              description: "Not authorized to perform requested action"
              body:
                text/plain:
        put:
          is: [validate]
          responses:
            400:
              description: "Bad request"
              body:
                text/plain:
            401:
              description: "Not authorized to perform requested action"
              body:
                text/plain:
        delete:
          is: [ validate ]
          responses:
            400:
              description: "Bad request"
              body:
                text/plain:
            401:
              description: "Not authorized to perform requested action"
              body:
                text/plain:
      /natalia:
          get:
            responses:
              200:
                body:
                  text/plain:
                    example: "OK"
              400:
                description: "Bad request"
                body:
                  text/plain:
                    example: "Bad request"
    
    
    
  2. Build the project

    mvn clean install
  3. Update the code of the module to match the changes in raml file


 Phase: Working with Docker image
  1. Build the Docker image inside of the module 

    docker build -t <name> <path>
    where <path> - location of *-fat.jar file and Dockerfile 

    info

    The command above will tag you image with default 'latest' tag.

    To change it use

    docker tag image <username>/<repository>:<tag>
    example
    docker tag d_notes<username>/<repository>:test
  2. Check local Docker images

    docker images

  3. Create an account in the Docker hub and then login in 

    docker login
  4. Publish the image with following command

    docker push username/repository:tag

    After successful push you will be able to see following

     

  5. Run Vagrant box

    vagrant up
    vagrant ssh
  6.  Navigate to Vagrant shared folder and pull Docker image

    cd /vagrant/.vagrant
    docker pull <user_name>/<repository>
    Example: docker pull b3946935/d_notes
  7. Find the Container Id of the module you want to replace

  8. Stop the Docker container

    docker stop <container_id>


 Phase: Module deployment
  1. Publish the Module descriptor

    http://localhost:9130/_/proxy/modules
     sample Module descriptor
    {
      "id": "mod-notes-2.1.1-SNAPSHOT",
      "name": "Notes",
      "requires": [
        {
          "id": "users",
          "version": "14.0 15.0"
        },
        {
          "id": "notify",
          "version": "1.1"
        }
      ],
      "provides": [
        {
          "id": "notes",
          "version": "1.0",
          "handlers": [
            {
              "methods": ["GET"],
              "pathPattern": "/notes",
              "permissionsRequired": ["notes.collection.get"],
              "permissionsDesired": ["notes.domain.*", "notes.domain.all"]
            },
            {
              "methods": ["GET"],
              "pathPattern": "/notes/natalia"
            },
            {
              "methods": ["POST"],
              "pathPattern": "/notes",
              "permissionsRequired": ["notes.item.post"],
              "permissionsDesired": ["notes.domain.*", "notes.domain.all"]
            },
            {
              "methods": ["GET"],
              "pathPattern": "/notes/_self",
              "permissionsRequired": ["notes.collection.get"],
              "permissionsDesired": ["notes.domain.*", "notes.domain.all"]
            },
            {
              "methods": ["GET"],
              "pathPattern": "/notes/{id}",
              "permissionsRequired": ["notes.item.get"],
              "permissionsDesired": ["notes.domain.*", "notes.domain.all"]
            },
            {
              "methods": ["PUT"],
              "pathPattern": "/notes/{id}",
              "permissionsRequired": ["notes.item.put"],
              "permissionsDesired": ["notes.domain.*", "notes.domain.all"]
            },
            {
              "methods": ["DELETE"],
              "pathPattern": "/notes/{id}",
              "permissionsRequired": ["notes.item.delete"],
              "permissionsDesired": ["notes.domain.*", "notes.domain.all"]
            }
          ]
        },
        {
          "id": "_tenant",
          "version": "1.0",
          "interfaceType": "system",
          "handlers": [
            {
              "methods": [
                "POST"
              ],
              "pathPattern": "/_/tenant"
            },
            {
              "methods": [
                "DELETE"
              ],
              "pathPattern": "/_/tenant"
            }
          ]
        }
      ],
      "permissionSets": [
        {
          "permissionName": "notes.collection.get",
          "displayName": "Notes - get notes collection",
          "description": "Get notes collection"
        },
        {
          "permissionName": "notes.item.get",
          "displayName": "Notes - get individual note from storage",
          "description": "Get individual note"
        },
        {
          "permissionName": "notes.item.post",
          "displayName": "Notes - create note",
          "description": "Create note"
        },
        {
          "permissionName": "notes.item.put",
          "displayName": "Notes - modify note",
          "description": "Modify note"
        },
        {
          "permissionName": "notes.item.delete",
          "displayName": "Notes - delete note",
          "description": "Delete note"
        },
        {
          "permissionName": "notes.domain.all",
          "displayName": "Notes - allow access to all domains",
          "description": "All domains"
        },
        {
          "permissionName": "notes.allops",
          "displayName": "Notes module - all CRUD permissions",
          "description": "Entire set of permissions needed to use the notes modules, but no domain permissions",
          "subPermissions": [
            "notes.collection.get",
            "notes.item.get",
            "notes.item.post",
            "notes.item.put",
            "notes.item.delete"
          ],
          "visible": false
        },
        {
          "permissionName": "notes.all",
          "displayName": "Notes module - all permissions and all domains",
          "description": "Entire set of permissions needed to use the notes modules on any domain",
          "subPermissions": [
            "notes.allops",
            "notes.domain.all"
          ],
          "visible": false
        }
      ]
    }
    
    
    
  2. Publish Deployments descriptor

    http://localhost:9130/_/discovery/modules

    The Deployment descriptor for Docker image is little different

    {
      "srvcId" : "mod-notes-2.1.1-SNAPSHOT",
      "nodeId" : "10.0.2.15",
      "descriptor" : {
        "dockerImage" : "b3946935/d_notes:latest",
        "dockerArgs" : {
          "HostConfig" : { "PortBindings": { "8081/tcp": [{ "HostPort": "%p" }] } }
        },
        "env" : [
              { "name" : "db.host", "value" : "10.0.2.15" },          
              { "name" : "db.port", "value" : "5432" },          
              { "name" : "db.username", "value" : "folio_admin" },          
              { "name" : "db.password", "value" : "folio_admin" },          
              { "name" : "db.database", "value" : "okapi_modules" },          
              { "name" : "db.maxPoolSize", "value" : "5" },          
              { "name" : "JAVA_OPTIONS", "value" : "-Xmx256m" }        
              ],
        "dockerPull" : false
          }
    }
    "nodeId" - IP of the running Vagrant instance. You may want to use the following command to find it. 
    vagrant ssh -c "ip address show eth0 | grep 'inet ' | sed -e 's/^.*inet //' -e 's/\/.*$//'"
    "dockerImage" - the source, for docker images built locally use name used for 'docker build -t <name> <path>' command

    Deployment descriptors of the modules included into Vagrant box are available in folder

    /etc/folio/deployment-descriptors
  3. Enable the module for the tenant

    http://localhost:9130/_/proxy/tenants/diku/modules

  4. Check the work of the new endpoint