Prerequisites

Before proceeding with the tutorial, please install the following software:

  • Oracle VM VirtualBox
  • HashiCorp Vagrant
  • Avid Platform Virtual Machine

For instructions on setting up VirtualBox, Vagrant, and the Avid Platform Virtual Machine, see the README included in the Avid Platform Virtual Machine download file (avid-platform-vagrant-vm-YYYY-MM-DD-XXXX.zip).

In addtion, make sure the node and npm commands are available in your shell.

If running on Windows, ensure that Python 2.7 is installed and the PYTHON environment variable is set (needed for npm to install packages correctly).

Creating the Project Structure

  1. Make sure you are in the root directory of the unzipped avid-connector-api-node. Create a directory for your project. We will use the name node-service in this tutorial. Then create module subfolder inside.

    1
    2
    mkdir node-service
    mkdir node-service/module
  2. Make node-service/module directory current

    1
    cd node-service/module
  3. Create a package.json file

    1
    touch package.json
  4. Add the following content to this file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    "name": "node-service",
    "description": "Tutorial service based on Avid Connector API for Node.js.",
    "version": "0.0.1",
    "devDependencies": {
    "proxy-bal": "../../node_modules/proxy-bal.tgz"
    },
    "main": "./index.js",
    "engines": {
    "node": ">= 0.12.0"
    }
    }
  5. Create lib and bin directories:

    1
    2
    mkdir lib
    mkdir bin
  6. Install NPM dependencies:

    1
    npm install

Congratulations! A simple project layout and core dependencies are now ready.

Connecting to the Platform

  1. Create a bin/node-service.js file:

    1
    touch bin/node-service.js
  2. Add the following content to bin/node-service.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #!/usr/bin/env node

    var bal = require('proxy-bal');

    var access = bal.createAccess();

    access.on('connected', function() {
    bal.logger.info("Connected to platform")
    });

    access.connect();

    access.on('disconnected', function () {
    process.exit(0);
    });

    process.on('SIGTERM', function() {
    access.disconnect();
    });
  3. Set environment variables to use a websocket connection with a protobuf payload:

    1
    export ACS_GATEWAY_HOST=avid-platform-zone1
  4. Run the service you just created:

    1
    node bin/node-service.js

    You should see following logs output in the shell:

    1
    2
    2016-02-11T23:26:56,390-0500 - info: Node Proxy BAL v3.5.2
    2016-02-11T23:26:56,586-0500 - info: Connected to platform
  5. Press Ctrl+C to terminate the service.

Congratulations! You have successfully connected to Platform as a client.

Creating a Service

  1. Create index.js, lib/node-service.json and lib/node-service.js files:

    1
    2
    3
    touch index.js
    touch lib/node-service.json
    touch lib/node-service.js
  2. Add the following content to lib/node-service.json:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    {
    "serviceType": "avid.tutorial.node.service",
    "serviceRealm": "global",
    "serviceVersion": 1,
    "description": "Tutorial service based on Avid Connector API for Node.js.",
    "ops": {
    "submitJob": {
    "description": "Submit job for execution. Returns response back with information whether job submitted successfully or not",
    "examples": {
    "Submit valid job": {
    "jobId": 1,
    "execTime": 1000
    },
    "Submit invalid jobId": {
    "jobId": -1,
    "execTime": 1000
    },
    "Submit invalid execTime": {
    "jobId": 2,
    "execTime": 10
    }
    },
    "rest": {
    "path": "jobs/{jobId}",
    "queryParams": ["execTime"],
    "method": "POST"
    }
    }
    },
    "errorCodes": {
    "INVALID_JOB_ID": {
    "status": 500,
    "severity": "ERROR",
    "messageTemplate": "Invalid jobId: %{jobId}",
    "origin": "SERVICE"
    },
    "INVALID_EXECUTION_TIME": {
    "status": 500,
    "severity": "ERROR",
    "messageTemplate": "Invalid execution time: %{execTime}",
    "origin": "SERVICE"
    }
    }
    }
  3. Add the following content to lib/node-service.js:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    var util         = require('util');
    var bal = require('proxy-bal');
    var node_service = require('./node-service.json');

    var NodeService = function(bus_access, options) {
    NodeService.super_.call(
    this,
    bus_access,
    node_service,
    options);

    this.on('configured', function(config, service, configurationUpdate, err) {
    this.logger.info("GOT CONFIGURATION");
    if (config) {
    this.config = config;
    }
    });

    return this;
    };
    util.inherits(NodeService, bal.Service);

    NodeService.prototype.submitJob = function(m, responder) {
    var jobId = parseInt(m.paramSet.jobId);
    var execTime = parseInt(m.paramSet.execTime);

    if (isNaN(jobId) || jobId < 1) {
    return responder.error({
    code: "INVALID_JOB_ID",
    params: {
    jobId: m.paramSet.jobId
    },
    details: "Invalid jobId was provided. Must be integer > 0."
    });
    }

    if (isNaN(execTime) || execTime < 100) {
    return responder.error({
    code: "INVALID_EXECUTION_TIME",
    params: {
    execTime: m.paramSet.execTime
    },
    details: "Invalid execution time provided. Must be integer >= 100."
    });
    }

    responder.reply({
    jobStatus: "submitted"
    });
    };

    module.exports = function (ctrl, options) {
    return new NodeService(ctrl, options);
    }
  4. Add the following content to the index.js file:

    1
    module.exports = require('./lib/node-service.js');
  5. Update the bin/node-service.js file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #!/usr/bin/env node

    var bal = require('proxy-bal');
    var Service = require('../index');

    var access = bal.createAccess();

    access.on('connected', function() {
    bal.logger.info("Connected to platform")
    var service = new Service(access);

    var onRegistered = function(err, service) {
    if (err) {
    bal.logger.info("Failed to register service. Error message: " + err.errorMessage);
    } else {
    bal.logger.info("Service is registered successfully");
    }
    };

    access.registerService(service, {}, onRegistered)
    });

    access.connect();

    access.on('disconnected', function () {
    process.exit(0);
    });

    process.on('SIGTERM', function() {
    access.disconnect();
    });
  6. Set environment variables to use a websocket connection with protobuf payload:

    1
    export ACS_GATEWAY_HOST=avid-platform-zone1
  7. Run the service:

    1
    node bin/node-service.js

    You should see logs output in the shell.

  8. Test that the service is working:

    • Open the ACS Monitor. Filter the services list by entering node\.service in the Service Types Filter field and clicking the Apply button. The list is reduced to avid.tutorial.node.service.
    • Expand the Service Operations List for the service. The list should include the submitJob operation.
    • Click on the Submit valid job link. Its corresponding message should appear in the code editor area, under the Request to field.
    • Click the Query button. Your service’s response appears in the Response field.
  9. Press Ctrl+C in the shell to terminate the service.

Congratulations! You have successfully registered your service on the Platform.

Sending Requests to Another Service

  1. In this procedure you modify the NodeService.prototype.submitJob method in lib/node-service.js, and add two new methods, NodeService.prototype.setNumberOfSubmittedJjobs and NodeService.prototype.getNumberOfSubmittedJobs:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    NodeService.prototype.submitJob = function(m, responder) {
    var self = this;

    var jobId = parseInt(m.paramSet.jobId);
    var execTime = parseInt(m.paramSet.execTime);

    if (isNaN(jobId) || jobId < 1) {
    return responder.error({
    code: "INVALID_JOB_ID",
    params: {
    jobId: m.paramSet.jobId
    },
    details: "Invalid jobId was provided. Must be integer > 0."
    });
    }

    if (isNaN(execTime) || execTime < 100) {
    return responder.error({
    code: "INVALID_EXECUTION_TIME",
    params: {
    execTime: m.paramSet.execTime
    },
    details: "Invalid execution time provided. Must be integer >= 100."
    });
    }

    var sendResponse = function(number) {
    responder.reply({
    jobStatus: "submitted",
    numberOfSubmittedJobs: number
    });
    };

    self.getNumberOfSubmittedJobs(function(err, number) {
    var newNumber = number;

    if (!err) {
    self.setNumberOfSubmittedJobs(++newNumber, function(err) {
    if (err) {
    sendResponse(number);
    } else {
    sendResponse(newNumber);
    }
    });
    } else if (err instanceof Array && err[0] && err[0].code === 'avid.acs.attributes/NOT_FOUND') {
    newNumber = 1;
    self.setNumberOfSubmittedJobs(newNumber, function(err) {
    if (err) {
    sendResponse(null);
    } else {
    sendResponse(newNumber);
    }
    });
    } else {
    sendResponse(null);
    }
    });
    };

    NodeService.prototype.getNumberOfSubmittedJobs = function(callback) {
    var self = this;

    var message = {
    serviceType: 'avid.acs.attributes',
    serviceRealm: 'global',
    serviceVersion: '3',
    op: 'fetch',
    paramSet: {
    name: 'node.service.store'
    }
    };

    var options = {
    timeout: 2000
    };

    self.bus.advancedQuery(message, options, function(reply) {
    if (reply.errorSet) {
    self.logger.info("Error received trying get number of submitted jobs: %s", JSON.stringify(reply.errorSet));
    callback(reply.errorSet, null);
    } else {
    var numberOfSubmittedJobs = reply.resultSet.value.numberOfSubmittedJobs;
    self.logger.info("Nubmer of submitted jobs: %d", numberOfSubmittedJobs);
    callback(null, numberOfSubmittedJobs);
    }
    }, function() {
    self.logger.info("Timeout occurred");
    callback("Timeout", null);
    }, function(error) {
    self.logger.info("Error occurred. Error message: " + err.errorMessage);
    callback("Error", null);
    });
    }

    NodeService.prototype.setNumberOfSubmittedJobs = function(number, callback) {
    var self = this;

    var message = {
    serviceType: 'avid.acs.attributes',
    serviceRealm: 'global',
    serviceVersion: '3',
    op: 'store',
    paramSet: {
    name: 'node.service.store',
    value: {
    numberOfSubmittedJobs: number
    }
    }
    };

    var options = {
    timeout: 2000
    };

    self.bus.advancedQuery(message, options, function(reply) {
    if (reply.errorSet) {
    self.logger.info("Error received trying update nubmer of submitted jobs: %s", JSON.stringify(reply.errorSet));
    callback(reply.errorSet);
    } else {
    self.logger.info("Number of submitted jobs updated to %d", number);
    callback(null);
    }
    }, function() {
    self.logger.info("Timeout occurred");
    callback("Timeout", null);
    }, function(error) {
    self.logger.info("Error occurred. Error message: " + err.errorMessage);
    callback("Error", null);
    });
    }
  2. Set environment variables to use a websocket connection with a protobuf payload:

    1
    export ACS_GATEWAY_HOST=avid-platform-zone1
  3. Run the service:

    1
    node bin/node-service.js

    You should see logs output in the shell.

  4. Test that the service is working. Open ACS Monitor, and send several submitJob requests to your service. Each time you send a new request, you should see the number of submitted jobs incrementing in the response:

    1
    2
    3
    4
    5
    6
    {
    "resultSet": {
    "jobStatus": "submitted",
    "numberOfSubmittedJobs": 2
    }
    }
  5. Press Ctrl+C in the shell to terminate the service.

Congratulations! You have successfully sent a request to another service, and received a response from it.

Posting Events when the Job is Submitted and Completed

  1. Edit lib/node-service.js file. Here, we modify the NodeService.prototype.submitJob method:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    NodeService.prototype.submitJob = function(m, responder) {
    var self = this;

    var jobId = parseInt(m.paramSet.jobId);
    var execTime = parseInt(m.paramSet.execTime);

    if (isNaN(jobId) || jobId < 1) {
    return responder.error({
    code: "INVALID_JOB_ID",
    params: {
    jobId: m.paramSet.jobId
    },
    details: "Invalid jobId was provided. Must be integer > 0."
    });
    }

    if (isNaN(execTime) || execTime < 100) {
    return responder.error({
    code: "INVALID_EXECUTION_TIME",
    params: {
    execTime: m.paramSet.execTime
    },
    details: "Invalid execution time provided. Must be integer >= 100."
    });
    }

    var sendResponse = function(number) {
    responder.reply({
    jobStatus: "submitted",
    numberOfSubmittedJobs: number
    });
    };

    var submittedMsg = {
    channel: 'node.service.jobs',
    subject: 'submit',
    dataSet: {
    jobId: jobId
    }
    };

    var callback = function(err) {
    if (err) {
    console.err("Error message: " + err.errorMessage);
    console.err("Error type: " + err.errorType);
    } else {
    console.log("Successfully post to channel");
    }
    };

    self.bus.postToChannel(submittedMsg, callback);

    setTimeout(function() {
    var completeMsg = {
    channel: 'node.service.jobs',
    subject: 'complete',
    dataSet: {
    jobId: jobId
    }
    };

    self.bus.postToChannel(completeMsg, callback);
    }, execTime);

    self.getNumberOfSubmittedJobs(function(err, number) {
    var newNumber = number;

    if (!err) {
    self.setNumberOfSubmittedJobs(++newNumber, function(err) {
    if (err) {
    sendResponse(number);
    } else {
    sendResponse(newNumber);
    }
    });
    } else if (err instanceof Array && err[0] && err[0].code === 'avid.acs.attributes/NOT_FOUND') {
    newNumber = 1;
    self.setNumberOfSubmittedJobs(newNumber, function(err) {
    if (err) {
    sendResponse(null);
    } else {
    sendResponse(newNumber);
    }
    });
    } else {
    sendResponse(null);
    }
    });
    };
  2. Set environment variables to use a websocket connection with a protobuf payload:

    1
    export ACS_GATEWAY_HOST=avid-platform-zone1
  3. Run the service:

    1
    node bin/node-service.js

    You should see logs output in the shell.

  4. Press Ctrl+C in the shell to terminate the service.

Congratulations! You have successfully published events to the Platform.

At this point, service behavior in the ACS Monitor is unchanged. Next, we create a consumer of our service events.

Subscribing to Service Events

Other services and clients may wish to listen on the channel to which our service posts, so they can receive event information. We demonstrate this functionality by creating a new client that listens on the channel.

  1. Create a bin/node-client.js file:

  2. Add the following content to the file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #!/usr/bin/env node

    var bal = require('proxy-bal');

    var access = bal.createAccess();

    access.on('connected', function() {
    bal.logger.info("Connected to platform")

    access.subscribeToChannel('node.service.jobs', [ 'submit' ],
    function(err, message) {
    if (!err) {
    bal.logger.info("Job %d is submitted", message.dataSet.jobId);
    }
    },
    function(err, subscriberId) {
    if (err) {
    bal.logger.info("Failed to subscribe to node.service.jobs - submit. Error message: " + err.errorMessage);
    } else {
    bal.logger.info("Subscribed to node.service.jobs - submit");
    }
    });

    access.subscribeToChannel('node.service.jobs', [ 'complete' ],
    function(err, message) {
    if (!err) {
    bal.logger.info("Job %d is completed", message.dataSet.jobId);
    }
    },
    function(err, subscriberId) {
    if (err) {
    bal.logger.info("Failed to subscribe to node.service.jobs - complete. Error message: " + err.errorMessage);
    } else {
    bal.logger.info("Subscribed to node.service.jobs - complete");
    }
    });
    });

    access.connect();

    access.on('disconnected', function () {
    process.exit(0);
    });

    process.on('SIGTERM', function() {
    access.disconnect();
    });
  3. Set environment variables to use a websocket connection with a protobuf payload:

    1
    export ACS_GATEWAY_HOST=avid-platform-zone1
  4. Run the service:

    1
    node bin/node-service.js
  5. Open a new shell in the same location. Set environment variables to use a websocket connection with a protobuf payload:

    1
    export ACS_GATEWAY_HOST=avid-platform-zone1
  6. Run the client:

    1
    node bin/node-client.js
  7. Test that the service and client are working:

    • Open ACS Monitor, and filter the services list by entering the node\.service regular expression in the box. You should see now only one service in the list: avid.tutorial.node.service.
    • Send multiple submitJob messages to your service with different values. Examine the logs of your client. The logs ought to contain each “job submit” and “job complete” event.

Congratulations! You have created a client that subscribes to the events being posted by your service.

Sending REST Requests to Your Service

  1. Log in to avid-platform-zone1 with ssh (username: root; password: vagrant):

    1
    ssh root@avid-platform-zone1
  2. Send a POST request to your service using the curl command. Use the avidAccessToken generated previously:

    1
    2
    3
    curl -H "Content-Type: application/json" --data '{"execTime":15000}' "http://avid-platform-zone1:8080/apis/avid.tutorial.node.service;version=1/jobs/10?_avidAccessToken=ZWEwZDhlOTItYmJhZS00YTQ5LWFiN2QtNTAyYWMwZjhjZjJj"

    #NOTE: The request should be routed via NGINX and use the HTTPS protocol: https://avid-platform-zone1/apis/...

    Given HTTP request will send following message to your service:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "serviceType": "avid.tutorial.node.service",
    "serviceRealm": "global",
    "serviceVersion": 1,
    "op": "submitJob",
    "paramSet": {
    "jobId": 10,
    "execTime": 15000
    }
    }
  3. Press Ctrl+C in the shells to terminate both the service and client.

Delivering the Service as an RPM on CentOS 6.x/RedHat 6.x

For this tutorial and the next one, you need a CentOS 6.x/RedHat 6.x system. You may use your VM (created on the first steps of this tutorial) with the host name avid-platform-zone1. The following steps assume you are using this VM to test the RPM.

  1. At this point your current directory should be node-service/module. Go up one level. Your current directory must be node-service:

    1
    cd ..
  2. Create a node-service.spec file:

    1
    touch node-service.spec
  3. Add the following content to the file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    %global debug_package %{nil}

    Name: node-service
    Version: 1
    Release: 1
    Summary: Tutorial service based on Avid Connector API for Node.js
    License: Avid EULA
    Vendor: Avid Technology Inc.
    URL: http://avid.com
    Source: %{name}-src.tgz
    BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
    Prefix: /opt/avid

    AutoReqProv: no

    Requires: avid-acs-proxybal-node >= 3.5.0, avid-acs-proxybal-node < 4.0.0

    %description
    Tutorial service based on Avid Connector API for Node.js

    %build
    cd module
    npm install --production
    cd ..

    %prep
    %setup -c -n %{name}

    %install
    mkdir -p "${RPM_BUILD_ROOT}/opt/avid/sbin"
    mkdir -p "${RPM_BUILD_ROOT}/opt/avid/lib/node_modules"

    mv module "${RPM_BUILD_ROOT}/opt/avid/lib/node_modules/%{name}"
    ln -s /opt/avid/lib/node_modules/%{name}/bin/%{name}.js "${RPM_BUILD_ROOT}/opt/avid/sbin/%{name}"

    # Exit clean
    exit 0

    %files
    %attr(755, root, root) /opt/avid/lib/node_modules/%{name}/bin/%{name}.js
    /opt/avid/sbin/%{name}
    /opt/avid/lib/node_modules/%{name}
  4. Create a create-rmp.sh file:

    1
    touch create-rpm.sh
  5. Add the following content to the file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #!/bin/bash

    # Print usage if we have no args
    if [ $# -eq 0 ]; then
    echo 'Usage:'
    echo ' create-rpm <spec-file>'
    echo 'Description:'
    echo ' Used by jenkins jobs to create rpms. Specify'
    echo ' the spec file and this script will tar the'
    echo ' directory containing that file and run rpmbuild'
    echo ' in the parent dir. See source for details.'
    echo 'Examples:'
    echo ' ./create-rpm.sh controllers/registry/avid-acs-registry.spec'
    echo ' Creates ./rpmbuild/RPMS/x86_64/<rpm-name>.rpm'
    exit 0
    fi

    RPM_VERSION=${RPM_VERSION-"0.0.0_DEV"}
    if [ -f ./VERSION ]; then
    # file generated by jenkins job
    RPM_VERSION="$(cat ./VERSION)"
    fi

    ORIGINAL_DIR="$(pwd)"

    # The directory that we are going to tar
    SOURCE_DIR="$(dirname $1)"

    # The base name of the spec file. This name will be used
    # for the tar file.
    SPEC_NAME="$(basename $1 .spec)"

    # Set up rpmbuild tree
    mkdir -p rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}

    pushd "$SOURCE_DIR"

    # Compress sources and move into rpmbuild tree
    tar -czf "$SPEC_NAME-src.tgz" ./* --exclude=.acignore --exclude=node_modules --exclude=*.swp --exclude=*~
    mv "$SPEC_NAME-src.tgz" "$ORIGINAL_DIR/rpmbuild/SOURCES/"

    # Copy spec into rpmbuild tree
    cp "$SPEC_NAME.spec" "$ORIGINAL_DIR/rpmbuild/SPECS/"

    popd

    # Build rpm
    rpmbuild --define "_rpmversion $RPM_VERSION" --define "_topdir `pwd`/rpmbuild" -bb "rpmbuild/SPECS/$SPEC_NAME.spec"
  6. Make the script executable:

    1
    chmod 777 create-rpm.sh
  7. Remove the node-modules folder, to avoid duplicate copies of the NPM modules, which installed automatically when you issued the npm install command. Your current directory should be node-service:

    1
    rm -rf module/node_modules
  8. Copy the project to avid-platform-zone1 using ssh (username: root; password: vagrant). Your current directory should be node-service:

    1
    scp -r ../node-service root@avid-platform-zone1:/root/
  9. Log in to acs-zone-black using ssh (username: root; password: vagrant):

    1
    ssh root@avid-platform-zone1
  10. In the ssh shell switch the current directory:

    1
    cd /root/node-service
  11. If you created files on Windows, make sure to use the LF line separator for bash scripts; if not, convert CRLF to LF with the following command:

    1
    sed -i 's/\r//' create-rpm.sh

    Or, you can use vi or vim editor to change the file format by opening the file in the editor and then use :set ff=unix and then save the file.

  12. Install the needed RPM development tools:

    1
    yum install rpm-devel
  13. Create the RPM:

    1
    ./create-rpm.sh node-service.spec
  14. Install your RPM:

    1
    yum install rpmbuild/RPMS/x86_64/node-service-1-1.x86_64.rpm
  15. Run the installed service:

    1
    node /opt/avid/sbin/node-service
  16. Test how service is working in the ACS Monitor

  17. Press Ctrl+C to terminate service.

Congratulations! You have successfully created an RPM for your service, installed and started it.

Daemonizing the Service On CentOS 6.x/RedHat 6.x

In this section we daemonize the service, provide a default configuration for it, output logs into a file and add log rotation. If you are still in the ssh shell from the previous step, exit and return to your project directory. Your current directory must be node-service

  1. Modify the module/lib/node-service.js file. Add the following method, after util.inherits(NodeService, bal.Service);:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    NodeService.prototype.onInitialize = function (onInitialized) {
    var self = this;

    onInitialized();

    var lockFile = process.env['AVID_SERVICE_LOCK_FILE'];
    if (lockFile) {
    var fs = require("fs");
    fs.openSync(lockFile, 'w');
    fs.closeSync(fs.openSync(lockFile, 'w'));
    } else {
    self.logger.info("Lock file is not provided");
    }
    };
  2. Create etc, etc/init.d, etc/syconfig and etc/logrotate.d directories:

    1
    2
    3
    4
    mkdir etc
    mkdir etc/init.d
    mkdir etc/sysconfig
    mkdir etc/logrotate.d
  3. Create a node-service file in each of the etc/init.d, etc/syconfig and etc/logrotate.d directories:

    1
    2
    3
    touch etc/init.d/node-service
    touch etc/sysconfig/node-service
    touch etc/logrotate.d/node-service
  4. Add the following content to etc/sysconfig/node-service:

    1
    2
    3
    4
    #export ACS_GATEWAY_HOST=127.0.0.1
    #export ACS_GATEWAY_PORT=9900
    #export ACS_BUS_QUERY_TIMEOUT=10000
    #export ACS_LOG_LEVEL=info
  5. Add the following content to etc/logrotate.d/node-service:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /var/log/avid/node-service/node-service.log {
    size 100M
    rotate 10
    copytruncate
    missingok
    notifempty
    compress
    delaycompress
    }
  6. Add the following content to etc/init.d/node-service:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #!/bin/sh
    #
    # node-service Tutorial Node.js service
    #
    # chkconfig: - 86 15
    # description: Tutorial service based on Avid Connector API for Node.js
    #

    ### BEGIN INIT INFO
    # Provides: node-service
    # Required-Start: $local_fs $network $remote_fs
    # Required-Stop: $local_fs $network $remote_fs
    # Short-Description: Tutorial Node.js service
    # Description: Tutorial service based on Avid Connector API for Node.js
    #
    ### END INIT INFO

    AVID_SERVICE_NAME="node-service"
    AVID_SERVICE_PID_FILE="/var/run/avid/${AVID_SERVICE_NAME}/${AVID_SERVICE_NAME}.pid"
    AVID_SERVICE_START_CMD="/opt/avid/sbin/${AVID_SERVICE_NAME}"
    AVID_SERVICE_STDOUT_FILE="/var/log/avid/${AVID_SERVICE_NAME}/${AVID_SERVICE_NAME}.log"
    AVID_SERVICE_STDERR_FILE="/var/log/avid/${AVID_SERVICE_NAME}/${AVID_SERVICE_NAME}.err"
    AVID_SERVICE_LOCK_FILE="/var/lock/avid/${AVID_SERVICE_NAME}/${AVID_SERVICE_NAME}.lock"

    # Source service config file if it exists
    [ -e /etc/sysconfig/$AVID_SERVICE_NAME ] && . /etc/sysconfig/$AVID_SERVICE_NAME

    # Source function library.
    . /opt/avid/share/avid-daemonizer/avid-daemonizer.sh

    # Call into the generic case statement in acs-functions
    avid_svc_ctrl "$@"
  7. Modify node-service.spec:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    %global debug_package %{nil}

    Name: node-service
    Version: 2
    Release: 1
    Summary: Tutorial service based on Avid Connector API for Node.js
    License: Avid EULA
    Vendor: Avid Technology Inc.
    URL: http://avid.com
    Source: %{name}-src.tgz
    BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
    Prefix: /opt/avid

    AutoReqProv: no

    Requires: avid-daemonizer >= 3.0.0, avid-daemonizer < 3.1.0
    Requires: avid-acs-proxybal-node >= 3.5.0, avid-acs-proxybal-node < 4.0.0

    %description
    Tutorial service based on Avid Connector API for Node.js

    %build
    cd module
    npm install --production
    cd ..

    %prep
    %setup -c -n %{name}

    %install
    mkdir -p "${RPM_BUILD_ROOT}/opt/avid/sbin"
    mkdir -p "${RPM_BUILD_ROOT}/opt/avid/lib/node_modules"

    mv etc "${RPM_BUILD_ROOT}/"
    mv module "${RPM_BUILD_ROOT}/opt/avid/lib/node_modules/%{name}"
    ln -s /opt/avid/lib/node_modules/%{name}/bin/%{name}.js "${RPM_BUILD_ROOT}/opt/avid/sbin/%{name}"

    %post
    chkconfig --add %{name}

    # Exit clean
    exit 0

    %preun
    service %{name} stop ||:

    # If uninstalling, remove chkconfig entry
    if [ $1 -eq 0 ]; then
    chkconfig --del %{name}
    fi

    %postun
    # If upgrading, restart the service if it was already running
    if [ $1 -eq 1 ]; then
    chkconfig %{name} off
    service %{name} condrestart ||:
    fi

    %files
    %attr(755, root, root) /etc/init.d/%{name}
    %attr(644, root, root) /etc/logrotate.d/%{name}
    %attr(644, root, root) /etc/sysconfig/%{name}
    %attr(755, root, root) /opt/avid/lib/node_modules/%{name}/bin/%{name}.js
    /opt/avid/sbin/%{name}
    /opt/avid/lib/node_modules/%{name}
  8. Copy your project to avid-platform-zone1 using ssh (username: root; password: vagrant). Your current directory should be node-service:

    1
    scp -r ../node-service root@avid-platform-zone1:/root/
  9. Log in to acs-zone-black using ssh (username: root; password: vagrant):

    1
    ssh root@avid-platform-zone1
  10. In the ssh shell switch current directory:

    1
    cd /root/node-service
  11. If you created files on Windows, make sure to use the LF line separator for bash scripts; if not, convert CRLF to LF with the following command:

    1
    2
    3
    sed -i 's/\r//' create-rpm.sh
    sed -i 's/\r//' etc/init.d/node-service
    sed -i 's/\r//' module/bin/node-service.js

    Or, you can use vi or vim editor to change the file format by opening the file in the editor and then use :set ff=unix and then save the file.

  12. Create the RPM:

    1
    ./create-rpm.sh node-service.spec
  13. Install your RPM:

    1
    yum install rpmbuild/RPMS/x86_64/node-service-2-1.x86_64.rpm
  14. Run the installed service:

    1
    service node-service start
  15. Test that the service is working, using the ACS Monitor

  16. Monitor the service logs:

    1
    2
    3
    service node-service logs
    #or
    tail -f /var/log/avid/node-service/node-service.log

Congratulations! You have successfully completed this tutorial.