Skip to content

vRO project

Nikolay Blagoev edited this page Apr 20, 2021 · 4 revisions

Before you continue with this section validate that all of the prerequisites are met.

Use

Crate New vRO Project

You start by bootstraping a new project using one of the provided archetypes:

JS-Based Actions-Only Project

This project contains only actions as *.js files. It does not handle an end-to-end functionality, but is an excellent choice as a dependency for xml-based projects containing workflows, configurations and resource elements.

To create a new project of this type, you use the following command:

mvn archetype:generate \
    -DinteractiveMode=false \
    -DarchetypeGroupId=com.vmware.pscoe.o11n.archetypes \
    -DarchetypeArtifactId=package-actions-archetype \
    -DarchetypeVersion=<iac_for_vrealize_version> \
    -DgroupId=local.corp.it.cloud \
    -DartifactId=dns

This will generate the following project file structure:

dns
├── README.md
├── pom.xml
├── release.sh
└── src
    ├── main
    │   └── resources
    │       ├── local
    │       │   └── corp
    │       │       └── it
    │       │           └── cloud
    │       │               └── dns
    │       │                   └── sample.js
    │       ├── log4j.xml
    │       └── log4j2.xml
    └── test
        └── resources
            └── local
                └── corp
                    └── it
                        └── cloud
                            └── dns
                                └── SampleTests.js

You can delete the example action and test and start developing your code.

Every *.js file that follows the convention (see sample.js for reference) will be compiled into a vRO action where the action name is the name of the file (e.g. sample) and the module (namespace) is the path under src/main/resources (e.g. "local.corp.it.cloud.dns").

Actions in vRO are essentially functions that you can call by using System.getModule. In plain JavaScript, we model actions in the same way. Therefore, in the JS format it is required to have a single root function that represents the action. Since JavaScript is dynamically typed and vRO isn't, it is recommended to describe the parameter types and the return value type in JsDoc (see sample.js for reference). If you omit the JsDoc all types will be assumed to be be "Any".

Xml-Based Project

This is the standard vRO project that can cover all use cases. It contains workflows, resource elements, configuration elements, actions and policies and can have dependencies to any other vRO project type.

To create a new project of this type, you use the following command:

mvn archetype:generate \
    -DinteractiveMode=false \
    -DarchetypeGroupId=com.vmware.pscoe.o11n.archetypes \
    -DarchetypeArtifactId=package-xml-archetype \
    -DarchetypeVersion=<iac_for_vrealize_version> \
    -DgroupId=local.corp.it.cloud \
    -DartifactId=dns \
    -DworkflowsPath=Corp/Cloud/Util

This will produce the following project file structure:

util
├── pom.xml
├── release.sh
└── src
    └── main
        └── resources
            ├── Workflow
            │   └── Corp
            │       └── Cloud
            │           └── Util
            │               ├── Install.element_info.xml
            │               └── Install.xml
            └── dunes-meta-inf.xml

You can delete the example elements.

You need to build and import the package in vRO and start developing your code there. Make sure you sync the content from the vRO to your local file system regularly and commit your changes to SCM.

The Install workflow is the recommended way of automating the configuration/reconfiguration of your solution, so you can use the workflow generated by the archetype as a starting point and develop your installation code from there.

Mixed Project

Mixed project acts as virtual project combining the both JS-Based Actions-Only and XML-based projects under single unified structure. Such project type is useful for initial onboarding of existing vRO code into the toolchain or when there is no need for XML-based and JS-Based Actions-Only projects to have separate lifecycle.

To create a new project of this type, you use the following command:

mvn archetype:generate \
    -DinteractiveMode=false \
    -DarchetypeGroupId=com.vmware.pscoe.o11n.archetypes \
    -DarchetypeArtifactId=package-mixed-archetype \
    -DarchetypeVersion=<iac_for_vrealize_version> \
    -DgroupId=local.corp.it.cloud \
    -DartifactId=services \
    -DworkflowsPath=Corp/Cloud/Services

This will produce the following project file structure:

services
├── actions
│   ├── pom.xml
│   └── src
│       ├── main
│       │   └── resources
│       │       ├── local
│       │       │   └── corp
│       │       │       └── it
│       │       │           └── cloud
│       │       │               └── services
│       │       │                   └── sample.js
│       │       ├── log4j.xml
│       │       └── log4j2.xml
│       └── test
│           └── resources
│               └── local
│                   └── corp
│                       └── it
│                           └── cloud
│                               └── services
│                                   └── SampleTests.js
├── pom.xml
├── release.sh
└── workflows
    ├── pom.xml
    └── src
        └── main
            └── resources
                ├── Workflow
                │   └── Corp
                │       └── Cloud
                │           └── Services
                │               ├── Install.element_info.xml
                │               └── Install.xml
                └── dunes-meta-inf.xml

TypeScript-Based Project

This project contains actions as *.ts, workflows as *.wf.ts, configuration elements as *.config.ts and resource files.

To create a new project of this type, you use the following command:

mvn archetype:generate \
    -DinteractiveMode=false \
    -DarchetypeGroupId=com.vmware.pscoe.o11n.archetypes \
    -DarchetypeArtifactId=package-typescript-archetype \
    -DarchetypeVersion=<iac_for_vrealize_version> \
    -DgroupId=local.corp.it.cloud \
    -DartifactId=demo

This will generate the following project file structure:

demo
├── README.md
├── pom.xml
├── release.sh
└── src
    ├── sample.conf.ts
    ├── sample.test.ts
    ├── sample.ts
    └── sample.wf.ts

You can delete the examples and start developing your code.

Every *.ts file will be compiled into one of the corresponding types.

  • Actions (sample.ts) will be compiled into the module < groupId >.< artifactId >.<the path under src/ folder>
  • Tests (sample.test.ts) will not be added to the vRO package, but will be compiled into Javascript and triggered the same way as for the JS based projects
  • Configuration Elements (sample.conf.ts) and Workflows (sample.wf.ts) are going to be defined under the < artifactId >.<the path under src/ folder> category

Examples

Configuration Element
export interface SampleConfig {
    field1: string;
    field2: number;
    field3: any;
}
Workflow
import { Workflow, Out } from "tsc-annotations";

@Workflow({
    id: "c9be5b70-a650-43d0-a8a3-e39f731b055b",
    version: "1.0.0"
})
export class SampleWorkflow {
    public install(foo: string, bar: string, @Out result: any): void {
        System.log(`foo=${foo}, bar=${bar}`);
        result = "result value";
    }
}
Class
export class SampleClass {
    public sum(x: number, y: number) {
        return x + y;
    }
}
Test
import { SampleClass } from "./sample"

describe("Tests", () => {
    it("should sum two numbers", () => {
        expect(new SampleClass().sum(1, 2)).toBe(3)
    })
})
Action
import { LoggerFactory } from "./LoggerFactory";
/**
 *
 * @param name
 * @param options
 */
(function getLogger(name:string, options?:any) {
	return new LoggerFactory().getLogger(name, options);
});

Unit Testing

If you use js-based actions-only project, you can create unit tests to help you develop and verify your code.

The tests should be written using Jasmine, as one would do with any other JavaScript code. The only difference is that there are a number of vRO scriptable objects (e.g. ConfigurationElement) that are not present in the enulated vRO scripting context. You will have to mock those by overriding variables in the global scope.

For this project type, there is a Jasmine-JUnit adapter that is already configured. You place your tests under src/test following the same folder structure as with the actions (e.g. src/test/local/corp/it/cloud/dns).

An example test file is shown bellow. It tests a custom action for working with configuration elements and for that it overrides the Server and the ConfigurationElement scriptable object classes in the context:

describe("Something", function() {
    ConfigurationElement = function (name, attributes) {
        this.attributes = attributes;
        this.configurationElementCategory = null;
        this.description = "";
        this.name = name;
        this.version = "0.0.0";
        this.getAttributeWithKey = function getAttributeWithKey (key) {
            return this.attributes[key];
        };
        this.removeAttributeWithKey = function removeAttributeWithKey (key) {
            delete this.attributes[key];
        };
        this.setAttributeWithKey = function setAttributeWithKey (key, value) {
            this.attributes[key] = {key: key, value: value};
        };

        this.reload = function() {
            // all up-to-date
        };
    };
    Server = {
        query: jasmine.createSpy('query').and.returnValue(new ConfigurationElement("test"))
    }

    it("should set attribute", function() {
        System.getModule("local.corp.it.cloud.dns").setAttribute("Corp/Cloud/Util/test", "value", 1);
        var value = System.getModule("local.corp.it.cloud.dns").getAttribute("Corp/Cloud/Util/test", "value");
        expect(value).toBe(1);
    });
    it("should ...", function() {
      ...
    });
});

Note that all tests in a single file are executed in a single context, i.e. any changes to the global scope are persisted. However, different js files are executed in separate scripting contexts.

The Jasmine-JUnit adapter is part of the standard Maven lifecycle's testing phase, therefore tests will be discovered and executed every time you build your project. However, you can trigger tests explicitly:

mvn test # This will run all tests in the project without packaging it.
mvn test -Dtest=SomethingTest # This will run only tests in the SomethingTest.js file.

Building

You can build any vRO project from sources using Maven:

mvn clean package

This will produce a vRO package with the groupId, artifactId and version specified in the pom. For example:

<groupId>local.corp.it.cloud</groupId>
<artifactId>dns</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>package</packaging>

will result in local.corp.it.cloud.dns-1.0.0-SNAPSHOT.package generated in the target folder of your project.

In case you want to produce a bundle.zip containing the package and all its dependencies, you use:

mvn clean package -Pbundle

In case you want to include only a subset of the project tree you can use the "include" property specifying list of project relative locations to the files to include during packaging. Currently suported for both JS and XML type projects as well as for TS files producing actions (i.e. base .ts files).

mvn clean package -Dinclude="src/main/resources/com/vmware/package/action1.js,src/main/resources/com/vmware/package/action1.js"

This is useful in the case when multiple developers are working against same vRO and in order to limit the overlapping content updates. This way you can also get latest changes according to git diff:

mvn clean package -Dinclude="$(git diff --name-only origin/master | tr '\n' ',')"

Pull

When working on a vRO project, sometimes you might need to make changes on a live server using the vRO Client, e.g. when working on workflows.

Although this is applicable for both xml and js-based projects, it is more suitable for xml-based ones where you have the need to work on workflows, configuration and resource elements.

To support this use case, the toolchain comes with a custom goal "vro:pull". The following command will "pull" the package corresponding to the current project from a specified server and expand its content in the local filesystem overriding any local content:

vro:pull -Dvro.host=10.29.26.18 -Dvro.port=8281 [email protected] -Dvro.password=***

Sometimes you want to pull the content of another package into your project. This can be achieved by providing the remote package name as parameter.

vro:pull -DpackageName=com.vmware.pscoe.library.ssh -Dvro.host=10.29.26.18 -Dvro.port=8281 [email protected] -Dvro.password=***

A better approach is to have the different vRO/vRA development environments specified as profiles in the local settings.xml file by adding the following snippet under "profiles":

<servers>
    <server>
        <username>[email protected]</username>
        <password>{native+maven+encrypted+pass}</password>
        <id>corp-dev-vro</id>
    </server>
</servers>
....
<profile>
    <id>corp-dev</id>
    <properties>
        <!--vRO Connection-->
        <vro.host>10.29.26.18</vro.host>
        <vro.port>8281</vro.port>
        <vro.serverId>corp-dev-vro</vro.serverId>
        <vro.auth>vra</vro.auth>
        <vro.tenant>vsphere.local</vro.tenant>
    </properties>
</profile>

Then, you can sync content back to your local sources by simply activating the profile:

mvn vro:pull -Pcorp-env

Push

To deploy the code developed in the local project or checked out from source control to a live server, you can use the vrealize:push command:

mvn package vrealize:push -Pcorp-env

This will build the package and deploy it to the environment described in the corp-env profile. There are a few additional options.

Include Dependencies

By default, the vrealize:push goal will deploy all dependencies of the current project to the target environment. You can control that by the -DincludeDependencies flag. The value is true by default, so you skip the dependencies by executing the following:

mvn package vrealize:push -Pcorp-env -DincludeDependencies=false

Note that dependencies will not be deployed if the server has a newer version of the same package deployed. For example, if the current project depends on com.vmware.pscoe.example-2.4.0 and on the server there is com.vmware.pscoe.example-2.4.2, the package will not be downgraded. You can force that by adding the -Dvro.importOldVersions flag:

mvn package vrealize:push -Pcorp-env -Dvro.importOldVersions

The command above will forcefully deploy the exact versions of the dependent packages, downgrading anything it finds on the server.

Ignore Certificate Errors (Not recommended)

This section describes how to bypass a security feature in development/testing environment. Do not use those flags when targeting production servers. Instead, make sure the certificates have the correct CN, use FQDN to access the servers and add the certificates to Java's key store (i.e. cacerts).

You can ignore certificate errors, i.e. the certificate is not trusted, by adding the flag -Dvrealize.ssl.ignore.certificate:

mvn package vrealize:push -Pcorp-env -Dvrealize.ssl.ignore.certificate

You can ignore certificate hostname error, i.e. the CN does not match the actual hostname, by adding the flag -Dvrealize.ssl.ignore.certificate:

mvn package vrealize:push -Pcorp-env -Dvrealize.ssl.ignore.hostname

You can also combine the two options above.

The other option is to set the flags in your Maven's settings.xml file for a specific development environment.

<profile>
    <id>corp-dev</id>
    <properties>
        <!--vRO Connection-->
        <vro.host>10.29.26.18</vro.host>
        <vro.port>8281</vro.port>
        <vro.username>[email protected]</vro.username>
        <vro.password>***</vro.password>
        <vro.auth>vra</vro.auth>
        <vro.tenant>vsphere.local</vro.tenant>
        <vrealize.ssl.ignore.hostname>true</vrealize.ssl.ignore.hostname>
        <vrealize.ssl.ignore.certificate>true</vrealize.ssl.ignore.certificate>
    </properties>
</profile>

Bundling

To produce a bundle.zip containing the package and all its dependencies, use:

$ mvn clean deploy -Pbundle

Refer to vRealize Build Tools/Bundling for more information.

Clean Up

To clean up a version of vRO package from the server use:

  • Clean up only curent package version from the server
    mvn vrealize:clean -DcleanUpLastVersion=true -DcleanUpOldVersions=false -DincludeDependencies=false
    
  • Clean up curent package version from the server and its dependencies. This is a force removal operation.
    mvn vrealize:clean -DcleanUpLastVersion=true -DcleanUpOldVersions=false -DincludeDependencies=true
    
  • Clean up old package versions and the old vertions of package dependencies.
    mvn vrealize:clean -DcleanUpLastVersion=false -DcleanUpOldVersions=true -DincludeDependencies=true
    

Note that you can't have both -DcleanUpLastVersion=true and -DcleanUpOldVersions=true in a single command.

Troubleshooting

  • If Maven error does not contain enough information rerun it with -X debug flag.
mvn -X <rest of the command>
  • Sometimes Maven might cache old artifacts. Force fetching new artifacts with -U. Alternativelly remove /.m2/repository folder.
mvn -U <rest of the command>