Sunday, September 4, 2016

Add a new app type to the WSO2 App Cloud

This blog explain how you can add a new app type to the WSO2 App cloud.

Step 1: Get a clone of WSO2 app cloud code base
git clone https://github.com/wso2/app-cloud

Step 2: Create the docker files for the runtime
Please refer our existing docker files when creating new docker files for particular runtime.
https://github.com/wso2/app-cloud/tree/master/modules/resources/dockerfiles

Following blog post gives you some details about structure of the docker files
http://amalkas.blogspot.com/2016/09/add-new-runtime-to-existing-app-type-in.html

Step 3: Required database changes
When adding new app type you need add some database records, following diagram gives you an idea of database schema.

AC_CLOUD defines the cloud types
AC_APP_TYPE defines app types
AC_RUNTIME defines runtimes
AC_CONTAINER_SPECIFICATIONS defines container specs
AC_TRANSPORT defines the ports we expose for end users

-- insert app type
-- insert app type, cloud mapping
-- insert runtime
-- insert app type, runtime mapping
-- insert container spec if required
-- insert runtime, container spec mapping
-- insert transport if required
-- insert runtime, transport mapping


Step 4: Specify app-type meta data in app-types-properties.json file

This json file we used to load the app type details to the App Cloud UI.
Step 5: Add a sample
We need to add a sample to implement deploy sample option.

Commit your sample archive here:

Specify the sample location here with the property <app_type>_sample_artifact_url

Step 7: Implement endpoints section loading to the app home page
Please refer the followign blog post to see how we have developed the "Endpoints" section per app type.
http://amalkas.blogspot.com/2016/09/loading-endpoints-to-app-home-page-in.html

Loading endpoints to the app home page in WSO2 App Cloud

Currently we have 6 app types in WSO2 App Cloud and when we load a app home page of created applications we can see a section called "Endpoints". This blog post is about how we load endpoints per each and every app type and it's implementation

Loading endpoints for Java web application, Jaggery and PHP app types
for these 3 app types, user have to define the application context, when creating an application
as shown in below image.


Then we append that context to the app version url/default url and display in app home page as below.




Loading endpoints for Microservices app type
for microservices app type, microservices 2.0.0 itself provides a swagger url, we just display the swagger url here.



Loading endpoints for WSO2 dataservices app type
for dataservices app type, we need to invoke the ServiceAdmin soap service to get the endpoints. So we developed a axis2service and deployed in dss runtime, which invokes the ServiceAdmin and return the endpoints to the app cloud.

You can see the axis2Service here:
https://github.com/wso2/app-cloud/tree/master/modules/extensions/org.wso2.appcloud.dss.integration



Loading endpoints for WSO2 ESB app type
for ESB app type, we have developed an API and deployed in ESB runtime to get the SOAP and REST endpoints

You can see the code of the API here:
https://github.com/wso2/app-cloud/tree/master/modules/extensions/org.wso2.appcloud.esb.integration



Implementation
The implementation code we use to load endpoints per app types, you can find here.
https://github.com/wso2/app-cloud/blob/master/modules/jaggeryapps/appmgt/src/modules/application/endpoints.jag


per app type we need to define which mechanism we need to load to the app home page, that we have defined in app-types-properties.json file, as endpointExtractorObject property.

enabling or disabling appContext property, will show/hide Application Context field in Create Application page.

providing a value to the defaultContext will show a default context in Application Context field in Create Application page

Add a new runtime to a existing app type in WSO2 App Cloud

This blog post explains how you can add a new runtime to a existing app type. Recently WSO2 Carbon team released WSO2 AS 6.0.0 M3 release. I'm gonna explain how we can add this WSO2 AS 6.0.0 M3 as a new runtime of war app type.

Step 1: get a clone of app-cloud code base
git clone https://github.com/wso2/app-cloud


Step 2: Create required docker files
When creating docker files, please refer the existing docker files we have created for other runtimes and get an idea.

You can find the existing docker files here
https://github.com/wso2/app-cloud/tree/master/modules/resources/dockerfiles

You can find the wso2as docker files here. We need to add dockefiles related to WSO2AS 6.0.0 M3 dockerfiles here
https://github.com/wso2/app-cloud/tree/master/modules/resources/dockerfiles/wso2as



dockerfiles/wso2as/base 
This folder contains docker files required to build wso2as base images

dockerfiles/wso2as/default 
This folder contains docker files which require to build a images with wso2as base image and the war file we upload when we do create application.

dockerfiles/wso2as/url 
This folder contains docker files which require to build a images with wso2as base image and the war file we upload via url when we do create application.


Step 3: Database changes required
When adding a new runtime, you will need to update the database schema, following diagram explains the relationships with AC_RUNTIME table.

AC_RUNTIME defines the runtimes
AC_CONTAINER_SPECIFICATIONS defines the container specs we allow in our App Cloud setup
AC_TRANSPORT defines the ports we expose, to end users to access
AC_APP_TYPE defines the app types


Database queries required to add WSO2 AS 6.0.0 M3 runtime

-- add WSO2 AS 6.0.0 M3 runtime to the AC_RUNTIME table
INSERT INTO `AC_RUNTIME` (`id`, `name`, `repo_url`, `image_name`, `tag`, `description`) VALUES
(10, 'Apache Tomcat 8.0.28 / WSO2 Application Server 6.0.0-M3','registry.docker.appfactory.private.wso2.com:5000', 'wso2as', '6.0.0-m3', 'OS:Debian, JAVA Version:8u72');

-- add app type-runtime mapping
INSERT INTO `AC_APP_TYPE_RUNTIME` (`app_type_id`, `runtime_id`) VALUES
(1, 10);

-- add relavent container spec mappings
INSERT INTO `AC_RUNTIME_CONTAINER_SPECIFICATIONS` (`id`, `CON_SPEC_ID`) VALUES
(10, 3),
(10, 4);

-- add relavent transport mappings
INSERT INTO AC_RUNTIME_TRANSPORT (`transport_id`, `runtime_id`) VALUES
(3, 10),
(4, 10);

Step 4: integration above changes to App Cloud
Once you complete the above changes, you can send us a pull request :)


Generalising WSO2 App Cloud to Implement another cloud perspective (view)

Currently, WSO2 App Cloud follows the container based approach to provide different runtime to deploy application artifacts. Likewise we can follow the same approach to deploy ESB car file as a app type in WSO2 App Cloud. But, our requirement is to provide a separate cloud perspective (view) for end users for integration solutions, so we thought to generalize the app cloud to operate in two modes (as app cloud and integration cloud) on single app cloud deployment.



1. We need two different URLs (https://apps.cloud.wso2.com and https://integration.cloud.wso2.com ) to login to separate clouds.

How SSO works :
Currently, when a user login, it redirect to WSO2IS for SSO and then it comes to app cloud with https://apps.cloud.wso2.com url. Same as when user requests integration cloud it should redirect to WSO2IS and then it comes to the app cloud with https://integration.cloud.wso2.com url. For that we use 2 different issuers and 2 SPs configured in IS side. When the request first come to the app cloud, we find which cloud the user requests based on the host name, and then redirect the request to IS with correct issuer.

Loading relevant cloud view with valid titles, breadcrumbs, navigation buttons, etc ...:
Once the SSO happens we put the requested cloud to the session and then loads the UI with the correct UI elements reading the following json file.

{
    "app-cloud" : {
      "pageTitle": "WSO2 App Cloud",
      "cloudTitle" : "Application Cloud",
      "properties" : {
          "documentationUrl": "AppCloud.Documentation.Url",
          "supportUrl": "AppCloud.Support.Url”
      }
    },
    "integration-cloud" : {
      "pageTitle": "WSO2 Integration Cloud",
      "cloudTitle" : "Integration Cloud",
      "properties" : {
          "documentationUrl": "IntegrationCloud.Documentation.Url",
          "supportUrl": "IntegrationCloud.Support.Url”
      }
    }
}

2. Based on the selected cloud, app cloud should operate as follows.

- We want to differentiate app types per cloud.
- Application home page should list the application which was created in selected cloud.
- Application home page search function should work on application which was created in selected cloud only.
- Separate subscription plans required per cloud. [max number of applications and databases per cloud]
- Separate white listing required per cloud.

So we changed the app cloud database table structure as shown in below diagram and updated the implementation to get per cloud data.
With these changes we can deploy the app cloud as a separate deployment if required in future.




3. Unified UI design

Per app type, we will be require loading different UI components to the app home page.
As an example: How we display endpoints per app type. Different type of application provides different types of endpoints. ESB app types give SOAP and REST endpoints. Web/PHP gives just a web url. JAX-WS gives SOAP endpoint, etc…Likewise we will be required to add more UI components per app types. So we decided to go with unified UI design approach per app type with javascript abstraction layer. https://github.com/wso2/app-cloud/blob/master/modules/jaggeryapps/appmgt/src/modules/application/endpoints.jag

This is how we render endpoints per app type:
When user navigates to the app home page we make a call to the container and get the urls and generate the UI component to display in app home page.
We don’t persist these endpoints in database. So user can’t see the endpoints when the container is not up and running.


Tuesday, July 19, 2016

Create Application in WSO2 App Cloud via REST API

This blog post gives you some examples on how to use createApplication REST API correctly in WSO2 App Cloud https://docs.wso2.com/display/AppCloud/Published+APIs

Before invoke createApplication API, you need to login to the WSO2 App Cloud and get the session cookie.
curl -c cookies -v -k -X POST -k https://apps.cloud.wso2.com/appmgt/site/blocks/user/login/ajax/login.jag -d 'action=login&userName=amalka.wso2.com@<tenant>&password=<password>’

This are some basic example to create applications in WSO2 App Cloud.

Create application via upload from file system

curl -v -k -b cookies -X POST https://apps.cloud.wso2.com/appmgt/site/blocks/application/application.jag -F action=createApplication -F applicationName=SampleFile -F applicationDescription=desc -F runtime=1 -F appTypeName=war -F applicationRevision=1.0.0 -F uploadedFileName=sample.war -F runtimeProperties=[] -F tags=[]  -F fileupload=@/home/amalka/Downloads/sample.war -F isFileAttached=true -F conSpec=3 -F isNewVersion=false -F appCreationMethod=default

Create a version of the application created above

curl -v -k -b cookies -X POST https://apps.cloud.wso2.com/appmgt/site/blocks/application/application.jag -F action=createApplication -F applicationName=SampleFile -F applicationDescription=desc -F runtime=1 -F appTypeName=war -F applicationRevision=2.0.0 -F uploadedFileName=sample.war -F runtimeProperties=[] -F tags=[]  -F fileupload=@/home/amalka/Downloads/sample.war -F isFileAttached=true -F conSpec=3 -F isNewVersion=true -F appCreationMethod=default

Create application via upload from url

curl -v -k -b cookies -X POST https://apps.cloud.wso2.com/appmgt/site/blocks/application/application.jag -F action=createApplication -F applicationName=SampleURL -F applicationDescription=desc -F runtime=1 -F appTypeName=war -F applicationRevision=1.0.0 -F uploadedFileName=war_sample.war -F runtimeProperties=[] -F tags=[]  -F artifactUrl=https://github.com/wso2/app-cloud/raw/master/samples/artifacts/war_sample.war -F conSpec=3 -F isNewVersion=false -F appCreationMethod=url

Create application via clone GitHub repository

curl -v -k -b cookies -X POST https://apps.cloud.wso2.com/appmgt/site/blocks/application/application.jag -F action=createApplication -F applicationName=SampleGitHub -F applicationDescription=desc -F runtime=5 -F appTypeName=jaggery -F applicationRevision=1.0.0 -F runtimeProperties=[] -F tags=[]  -F conSpec=3 -F isNewVersion=false -F appCreationMethod=github -F gitRepoUrl=https://github.com/SabraO/DSSample.git -F gitRepoBranch=master -F projectRoot=/JaggerySample



action=createApplication
The action we are calling
applicationName=
Valid characters a-z A-Z 0-9 -
Space are not allowed
Numbers only names not allowed
Not case sensitive
applicationDescription=
Give a description for the application
appTypeName=
values [war, mss, php, jaggery, wso2dataservice]
runtime=
values [1, 2, 3, 4, 5, 6, 7]
applicationRevision=
format major.minor.patch
uploadedFileName=
Name of the file uploading
runtimeProperties=
Define runtime properties
[{"key":"enableAnalytics","value":"true"}]
tags=
Define tags
[{"key":"lifecycle","value":"development"}]
fileupload=
should starts with @
isFileAttached=
values [true, false]
conSpec=
values [1, 2, 3, 4, 5, 6, 7, 8]
isNewVersion=
values [true, false]
appCreationMethod=
values [default, url, github]
artifactUrl=
Specify url to download the artifact
gitRepoUrl=
Specify GitHub repository url
gitRepoBranch=
Specify GitHub repository branch
projectRoot=
Specify project root
applicationContext=
Specify application context.


Currently, WSO2 App Cloud supports number of app types, runtimes and container specifications. Following tables will help you to identify relevant runtimes, container specifications, supported app creation methods per app types.

Supported runtimes and app creation methods per app types


App type
Supported Runtime
Supported App creation method
1
war
1, 6
default, url
2
mss
2, 8
default, url
3
php
3
default, url, github
4
jaggery
5
default, url, github
5
wso2dataservice
7
default, url

Supported container specifications per runtimes


Runtimes
Supported Container specs
1
Apache Tomcat 8.0.28 / WSO2 Application Server 6.0.0-M1
3, 4
2
OpenJDK 8 + WSO2 MSF4J 1.0.0
2, 3, 4
3
Apache 2.4.10  
1, 2, 3
4
Carbon 4.2.0      

5
Jaggery 0.11.0
3, 4
6
Apache Tomcat 8.0.28 / WSO2 Application Server 6.0.0-M2
3, 4
7
WSO2 Data Services Server - 3.5.0
3, 4
8
OpenJDK 8 + WSO2 MSF4J 2.0.0
2, 3, 4

Container Specifications

Spec Id
Spec Name
CPU
Memory
1
128MB RAM and 0.1x vCPU
100
128
2
256MB RAM and 0.2x vCPU
200
256
3
512MB RAM and 0.3x vCPU
300
512
4
1024MB RAM and 0.5x vCPU
500
1024



Sunday, July 17, 2016

Log user events to audit.log file via Jaggery block layer

Let's say I have a Jaggery application deployed in WSO2 AS, I want to log the user events to audit.log file via Jaggery block layer.

We can log adding following code:

var audit = org.wso2.carbon.CarbonConstants.AUDIT_LOG;
audit.info("log message");

WSO2 App Cloud Architecture



App Cloud SaaS application provides an user interface & REST API for app cloud users to deploy their application artifacts. The web UI is developed by Jaggery, and it invokes the REST API, which invokes following backend components to provide the app cloud solution.


Docker Client provides an interface to build images and push to the docker registry.
Kubernetes Client provides an interface to deploy applications in kubernetes cluster
DAO provides an interface to manipulate database operations to store meta data required for App Cloud in App Cloud DB
SOAP Client uses to invoke WSO2 Storage Server admin services to create databases and users


WSO2 Application Server provides an hosting environment to deploy App Cloud SaaS application


WSO2 Identity Server provides identity management configuring SSO with App Cloud SaaS application


WSO2 Storage Server provides RSS instances for app cloud developers to store application data.


WSO2 Data Analytics Server collects statistics published by deployed applications and provides dashboards to the app cloud users.


Docker Registry uses to store application images created using the deployable artifacts and runtimes.


Kubernetes provides runtime for deploy applications. Kubernetes namespaces provides tenant isolation and per pod per container per application will be deployed


End users will access the deployed applications via HAProxy. Further it provides Default URL and Custom URL features apart from load balancing.

How commonauth service work with SSO in WSO2 Identity Server

Considering two SAML service providers configured under the same tenant, they are by default in single sing on, so after I login in Service provider A I can use the same browser to access Service provider B without inserting my credentials again. This blog explains in details how this happened with commonauth service during the second login

What happens here is:

On the first login to service provider A, it stores a cookie with a name "commonAuthId"
When the first request comes; DefaultRequestCoordinator.handle() [1] method invokes DefaultAuthenticationRequestHandler.handle() method, see [2]
within the DefaultAuthenticationRequestHandler.handle() it invokes concludeFlow() private method [3], that sets the 'commonAuthId' cookie via setAuthCookie() method [4]

private void setAuthCookie(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context,
                               String sessionKey, String tenantDomain) throws FrameworkException {
        Integer authCookieAge = null;

        if (context.isRememberMe()) {
            authCookieAge = IdPManagementUtil.getRememberMeTimeout(tenantDomain);
        }

        FrameworkUtils.storeAuthCookie(request, response, sessionKey, authCookieAge);
    }

FrameworkUtils.storeAuthCookie() method

public static void storeAuthCookie(HttpServletRequest req, HttpServletResponse resp, String id, Integer age) {

        Cookie authCookie = new Cookie(FrameworkConstants.COMMONAUTH_COOKIE, id);
        authCookie.setSecure(true);
        authCookie.setHttpOnly(true);
        authCookie.setPath("/");

        if (age != null) {
            authCookie.setMaxAge(age.intValue() * 60);
        }

        resp.addCookie(authCookie);
    }


[1] https://github.com/wso2/carbon-identity/blob/v5.0.7/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/DefaultRequestCoordinator.java#L80

[2] https://github.com/wso2/carbon-identity/blob/v5.0.7/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/DefaultRequestCoordinator.java#L135

[3] https://github.com/wso2/carbon-identity/blob/v5.0.7/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/DefaultAuthenticationRequestHandler.java#L120

[4] https://github.com/wso2/carbon-identity/blob/v5.0.7/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/DefaultAuthenticationRequestHandler.java#L284

Then, when we access the service provider B, it checks whether the "commonAuthId" is available in cookie list, if yes, then it gets the authentication details from SessionContext and by pass the authentication step.
see [5] findPreviousAuthenticatedSession() method

[5]https://github.com/wso2/carbon-identity/blob/v5.0.7/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/DefaultRequestCoordinator.java

Wednesday, July 13, 2016

Initial job has not accepted any resources; check your cluster UI to ensure that workers are registered and have sufficient resources

Sometimes you might see this WARN log continuously in DAS analyser nodes, This happened, the __spark_meta_table keeps some meta data info about the CAR files we deploy. When those information got corrupted, you can see this WARN log in console.

"Initial job has not accepted any resources; check your cluster UI to ensure that workers are registered and have sufficient resources"

This is a known issue in spark and PR is already given to the spark.

As a workaround we can remove the __spark_meta_table table and restart the node.
Since the  __spark_meta_table table is encrypted, we just can't delete it from database, we have to use the Analytics data backup and restore tool

Friday, June 10, 2016

CI/CD with WSO2 App Cloud, Jenkins & GitHub

One way to improve a developer’s productivity is to automate repetitive tasks, such as building, committing, deploying, and testing. In this tutorial you will learn how to configure Jenkins and GitHub to achieve continuous integration and continuous deployment with WSO2 App Cloud. A basic knowledge in using Maven, Git, and invoking REST APIs is recommended to follow these steps.

Introduction

The day-to-day life of a developer often involves some mundane and repetitive tasks like build, commit, deploy, test, etc. With evolution in technology, however, we can automate these tasks to make a developer’s life little easier as well as improve productivity.
Testing and deployment are two integral elements of software development. With some automation, those elements become solutions referred to as ‘continuous integration’ (CI) and ‘continuous deployment’ (CD). The ‘continuous’ aspect of these solutions means that your projects will be automatically tested and deployed, allowing you to focus more on writing code and less on setting up servers.
In this tutorial, we'll set up a popular CI server called Jenkins and integrate it with GitHub so it will run tests every time the new code is pushed. Thereafter, we'll set up Jenkins to automatically deploy the built artifacts to our app server, which will eliminate the need to deploy archives manually.
Here, we will be using WSO2 App Cloud to deploy our application. It provides a hosting environment to deploy different types of applications, such as web, PHP, Jaggery, and uploading a built archive, so we can completely eliminate the need of setting up servers to deploy an application.

[Note: If you’re looking to deploy different types of apps other than those supported by WSO2 App Cloud, please contact the WSO2 App Cloud team and we will consider including that app type to our next releases].




Pre-requisites

  1. We need something to test and deploy. We will be using a web app called HelloWebApp. It’s just display Hello World! in the browser.
  2. To do the development you need a development machine with Maven, Java, and Git installed.
  3. We need a GitHub account https://github.com/ with the HelloWebApp source repository https://github.com/amalkasubasinghe/HelloWebApp.
  4. Jenkins installed server with the required Maven configurations and plugins (Git plugin and GitHub plugin) installed https://jenkins.io/.
  5. To deploy the archives, you need an account in WSO2 App Cloud http://wso2.com/cloud/app-cloud/.


Jenkins and GitHub integration

Here, when the developer pushes the code to the GitHub, we need to trigger a build in Jenkins and then Jenkins will checkout the code, build it, and run the tests. How do we trigger a build in Jenkins? We have two options:
  1. GitHub triggers Jenkins via GitHub webhook when the developer pushes the code to GitHub.
  2. Jenkins polls GitHub periodically to check whether there is a new code push; if yes, then trigger Jenkins by itself.
Here, we will configure Jenkins to poll GitHub every hour and build the code and run the tests.

  1. Go to Jenkins > New Item
  2. Give Item name and select Maven project              
Figure 1

3. Give Maven project name, select GitHub and give project URL. Since we’re building the master and working on 1.0-SNAPSHOT, we names the project “HelloWebApp-1.0-SNAPSHOT”



Figure 2

4. Configure the Source Code Management section as shown in the image specifying the GitHub URL and the branch to build. Then enable the Poll SCM in the Build Trigger section and schedule to poll every hour.      

        


Figure 3


5. Configure the build section to build the source using Maven pom.



Figure 4

Note that in the sample project, what has been written does not include any tests, but you can add your tests to the src/test/java location. Then when Jenkins triggers a build, it will execute all your tests after building the code.

Jenkins and WSO2 App Cloud integration

The next step is how to deploy the built archives in WSO2 App Cloud. First, we will see how we can do this manually using WSO2 App Cloud published REST API. Refer here for more information.

1. We need to login to WSO2 App Cloud and get the session cookie. When you access an API via external REST client, first you need to invoke the login API to ensure that the user is authenticated and then the system stores the generated session cookie in a file, which we can use in the next API invocations.

curl -v -k -c cookies -X POST -d 'action=login&userName=amalka.wso2.com@amalka&password=password' https://apps.cloud.wso2.com/appmgt/site/blocks/user/login/ajax/login.jag

Here my username is amalka@wso2.com and the tenant domain is amalka.

2. Then create a new application passing the stored session cookie in the previous step. It will create an application in WSO2 App Cloud uploading HelloWebApp.war file.

curl -v -k -b cookies -X POST https://apps.cloud.wso2.com/appmgt/site/blocks/application/application.jag -F action=createApplication -F applicationName=HelloWebApp -F applicationDescription=desc -F runtime=1 -F appTypeName=war -F applicationRevision=1.0.0 -F uploadedFileName=HelloWebApp.war -F runtimeProperties=[] -F tags=[]  -F fileupload=@/home/amalka/A/MKT/github/HelloWebApp/target/HelloWebApp.war -F isFileAttached=true -F conSpecCpu=300 -F conSpecMemory=512 -F isNewVersion=false

We can simply automate the above application deployment process by using Maven or by using a shell script to execute these curl commands. Here we’re configuring Maven to deploy an application in WSO2 App Cloud. Later we can configure Jenkins to deploy the application using this Maven config.

Please note the latest version of WSO2 App Cloud does not support updating the same version of an already deployed application with the new code changes. Therefore, when Jenkins builds a new artifact, we need to deploy it as a new version of the particular application. In order to clearly identify the application that was deployed using the artifact built by the particular Jenkins build, we thought we’d append the Jenkins build number to the application version, e.g. if the build number is 53, the application version will be 1.0.53.


[Note that the ability to update the same version of an already deployed application will be available with WSO2 App Cloud in future releases].



    <modelVersion>4.0.0</modelVersion>
    <groupId>com.wso2.appcloud</groupId>
    <artifactId>HelloWebApp</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>HelloWebApp Maven Webapp</name>
    <url>http://maven.apache.org</url>
<dependencies>
    <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2</version>
                <executions>
                <execution>
                    <id>login</id>
                    <phase>deploy</phase>
                    <goals>
                        <goal>exec</goal>
                    </goals>
                    <configuration>
                            <executable>curl</executable>
                            <arguments>
                                <argument>-v</argument>
                                <argument>-k</argument>
                                <argument>-c</argument>
                                <argument>cookies</argument>
                                <argument>-X</argument>
                                <argument>POST</argument>
                                <argument>-F</argument>
                                <argument>action=login</argument>
                                <argument>-F</argument>
                                <argument>userName=${appcloud.username}</argument>
                                <argument>-F</argument>
                                <argument>password=${appcloud.password}</argument>
                                <argument>https://apps.cloud.wso2.com/appmgt/site/blocks/user/login/ajax/login.jag</argument>
                            </arguments>
                    </configuration>
                </execution>
                <execution>
                    <id>create application</id>
                    <phase>deploy</phase>
                    <goals>
                        <goal>exec</goal>
                    </goals>
                    <configuration>
                            <executable>curl</executable>
                            <arguments>
                                <argument>-v</argument>
                                <argument>-k</argument>
                                <argument>-b</argument>
                                <argument>cookies</argument>
                                <argument>-X</argument>
                                <argument>POST</argument>
                                <argument>https://apps.cloud.wso2.com/appmgt/site/blocks/application/application.jag</argument>
                                <argument>-F</argument>
                                <argument>action=createApplication</argument>
                                <argument>-F</argument>
                                <argument>applicationName=${project.artifactId}</argument>
                                <argument>-F</argument>
                                <argument>applicationDescription=${project.artifactId}_${project.version}_${build.number}</argument>
                                <argument>-F</argument>
                                <argument>conSpecMemory=512</argument>
                                <argument>-F</argument>
                                <argument>conSpecCpu=300</argument>
                                <argument>-F</argument>
                                <argument>runtime=1</argument>
                                <argument>-F</argument>
                                <argument>appTypeName=war</argument>
                                <argument>-F</argument>
                                <argument>applicationRevision=${deploy.version}.${build.number}</argument>
                                <argument>-F</argument>
                                <argument>uploadedFileName=${project.artifactId}-${project.version}.war</argument>
                                <argument>-F</argument>
                                <argument>runtimeProperties=[]</argument>
                                <argument>-F</argument>
                                <argument>tags=[{"key":"BUILD_NUMBER","value":"${build.number}"}]</argument>
                                <argument>-F</argument>
                                <argument>fileupload=@${project.build.directory}/${project.artifactId}-${project.version}.war</argument>
                                <argument>-F</argument>
                                <argument>isFileAttached=true</argument>
                                <argument>-F</argument>
                                <argument>isNewVersion=true</argument>
                            </arguments>
                    </configuration>
                </execution>
                </executions>
        </plugin>
        <plugin>
                <artifactId>maven-deploy-plugin</artifactId>
                <version>2.7</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
        </plugin>
    </plugins>
</build>
<properties>
        <deploy.version>1.0</deploy.version>
        <build.number>0</build.number>   
        <appcloud.password>password</appcloud.password>
        <appcloud.username>username</appcloud.username>
</properties>
</project>



  • In the above config, I have used Maven exec plugin to configure login and create application curl commands
  • Here, I have set the phase of the execution as deploy, since we don’t need to execute this commands every time we build the code (mvn clean install). So then login and create application commands will only execute when we run mvn deploy.
  • IBut when running mvn deploy will deploy the artifacts in nexus repo, that I have skipped adding following configuration to the Maven configuration
<plugin>
                <artifactId>maven-deploy-plugin</artifactId>
                <version>2.7</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
</plugin>



  • In the login command we need to pass the app cloud credentials to get the session cookie, but we can’t hard code those values in the Maven config since this code is publicly available in GitHub. Therefore, we’ve made those values configurable and we can set a username and password when running mvn deploy.
  • In create application command we have set the application revision appending the Jenkins build number as explained earlier; we need to pass that too when running mvn deploy.

  • <argument>applicationRevision=${deploy.version}.${build.number}</argument>

    We have set isNewVersion=true in create application command because each time we want to create a new version of the already deployed application. 

    <argument>isNewVersion=true</argument>

    We can set the app version specific information to the application via tags. We have set the Jenkins build number in this Maven config. 

    <argument>tags=[{"key":"BUILD_NUMBER","value":"${build.number}"}]</argument>

    Here is the command we need to execute to deploy an application in WSO2 App Cloud. We can pass the Jenkins build number as -Dbuild.number=${BUILD_NUMBER}

    mvn clean deploy -Dbuild.number=${BUILD_NUMBER} -Dappcloud.password=xxxxx -Dappcloud.username=amalka.wso2.com@amalka   

    Then you need to update the Jenkins config as follows. Update the build section with a new Maven command. Since Jenkins is not publicly accessible, we can configure our real credentials here. 

      


    Figure 5

    We have now successfully updated Maven configs and Jenkins to work with WSO2 App Cloud. Every hour, Jenkins will poll GitHub, and if there a new code push, Jenkins will check the code, build, and deploy a new version of the HelloWebApp application to WSO2 App Cloud.

    Working with WSO2 App Cloud

    Jenkins always creates a new version of HelloWebApp application. Therefore, before we run the first Jenkins build, we need to deploy the first version of the HelloWebApp in WSO2 App Cloud.

    Login to WSO2 App Cloud and create the first version of the HelloWebApp application providing the following details.
    Application Name: HelloWebApp
    Application Version: 1.0.0

    Build the HelloWebApp code locally and upload the HelloWebApp-1.0-SNAPSHOT.war


    Figure 6

    It will give you the Launch URL as http://amalka-hellowebapp-1-0-0.wso2apps.com/ and display Hello World in the browser.


    Figure 7

    Then add more content to the HelloWebApp code and push it to GitHub; then the next Jenkins job will deploy the updated artifact in WSO2 App Cloud as the new version 1.0.1.

    http://amalka-hellowebapp-1-0-1.wso2apps.com/



    Figure 8

    When the new app version is deployed in app cloud, the previous version will stop running. If you want to access the previous version, then you have to manually start the container and the currently running version will stop automatically. This is because WSO2 App Cloud allows to run only one version of the particular application at a time.

    [Please note that running number of versions of a particular application at once will be available in future releases].

    When you go to the HelloWebApp overview page you can see the deployed version of the HelloWebApp app as follows.



    Figure 9

    When you go a particular version you can see the tags we have set as shown in the image below.


    Figure 10

    Conclusion

    WSO2 App Cloud offers a readily available hosting environment to deploy different types of applications like web, PHP, MS4J, Jaggery; many more application types will be available in the future as well. The step-by-step instructions in this tutorial would have helped you to set up CI/CD in your environment with integration of Jenkins, GitHub, and WSO2 App Cloud.

    References