Integrating YaCy Grid Locally with Susper

The YaCy Grid is the second-generation implementation of YaCy, a peer-to-peer search engine.The search results can be improved to a great extent by using YaCy-Grid as the new backend for SUSPER. YaCy Grid is the best choice for distributed search topology. The legacy YaCy is made for decentralised and also distributed network. While both the networks are distributed,the YaCy-Grid is centralized and legacy YaCy is decentralized. YaCy Grid facilitates a lot with scaling that will be in our hand and can be done in all aspects​(loading, parsing, indexing) with computing power we choose. In YaCy,Solr is embedded. But in YaCy Grid,we will get elasticsearch cluster.​They are both built around the core underlying search library Lucene.But ​elasticsearch will help us to scale almost indefinitely. In this blog, I will show you how to integrate YaCy Grid with Susper locally and how to use it to fetch results.

Implementing YaCy Grid with Susper:

Before using YaCy Grid we need to first setup YaCy Grid and crawl to url using crawl start API, more information about that can be found here Implementing YaCy Grid with Susper and Setting up YaCy Grid locally.

So, once we are done with setup and crawling, we need to begin using its APIs in Susper. Following are some easy steps in which we can show results from YaCy Grid in a separate tab is Susper.

Step 1:

Creating a service to fetch results:

In order to fetch results from local YaCy Grid server we need to create a service to fetch results from local YaCy Grid server. Here is the class in grid-service.ts which fetches results for us.

export class GridSearchService {
 server = 'http://127.0.0.1:8100';
 searchURL = this.server + '/yacy/grid/mcp/index/yacysearch.json?query=';
 constructor(private http: Http,
             private jsonp: Jsonp,
             private store: Store<fromRoot.State>) {
 }
 getSearchResults(searchquery) { 
   return this.http
     .get(this.searchURL+searchquery).map(res =>
         res.json()
     ).catch(this.handleError);
 }

 

Step 2:

Modifying results.component.ts file

In order to get results from grid-service.ts in results.component.ts we must need to create an instance of the service and use this instance to get the results and store it in variables results.component.ts file and then use these variables to show results in results template. Following is the code that does this for us

ngOnInit() {
   this.grid.getSearchResults(this.searchdata.query).subscribe(res=>{
     this.gridResult=res.channels;
   });
 }

 

gridClick(){
   this.getPresentPage(1);
   this.resultDisplay = 'grid';
   this.totalgridresults=this.gridResult[0].totalResults;
   this.gridmessage='About ' + this.totalgridresults + ' results';
   this.gridItems=this.gridResult[0].items;
  
   console.log(this.gridItems);
 }

 

Step 3:

Creating a New tab to show results from YaCy Grid:

Now we need to create a tab in the template where we can use local variables in results.component.ts to show the results following the current design pattern here is the code for that

<li [class.active_view]="Display('grid')" (click)="gridClick()">YaCy_Grid</li>

<!--YaCy Grid-->
 <div class="container-fluid">
     <div class="result message-bar" *ngIf="totalgridresults > 0 && Display('grid')">
       {{gridmessage}}
     </div>
     <div class="autocorrect">
       <app-auto-correct [hidden]="hideAutoCorrect"></app-auto-correct>
     </div>
   </div>
 <div class="grid-result" *ngIf="Display('grid')">
   <div class="feed container">
       <div *ngFor="let item of gridItems" class="result">
         <div class="title">
           <a class="title-pointer" href="{{item.link}}" [style.color]="themeService.titleColor">{{item.title}}</a>
         </div>
         <div class="link">
           <p [style.color]="themeService.linkColor">{{item.link}}</p>
         </div>
         <div class="description">
           <p [style.color]="themeService.descriptionColor">{{item.pubDate|date:'MMMM d, yyyy'}} - {{item.description}}</p>
         </div>
       </div>
   </div>
 </div>
 <!-- END -->

 

Step 4:

Starting YaCy Grid Locally:

Now all we need is to start YaCy Grid server locally. To start it go in yacy_grid_mcp folder and use

python bin/start_elasticsearch.py

 

This will start elasticsearch from its respective script.Next use

python bin/start_rabbitmq.py

 

This will start RabbitMQ server with the required configuration.Next useThis will start elasticsearch from its respective script.Next use

gradle run

 

To start YaCy Grid locally.

Now we are all done we just need to start Susper using

ng serve

 

command and type a search query and move to YaCy_Grid tab to see results from YaCy Grid Server.

Here is the image which shows results from YaCy Grid in Susper

Resources

Removing vulnerable dependencies from SUSPER

A vulnerability is a problem in a project’s code that could be exploited to damage the confidentiality, integrity, or availability of the project or other projects that use its code. Depending on the severity level and the way your project uses the dependency, vulnerabilities can cause a range of problems for your project or the people who use it.GitHub tracks public vulnerabilities in Ruby gems and NPM packages on MITRE’s Common Vulnerabilities and Exposures (CVE) List.

What were  vulnerabilities in SUSPER ?

SUSPER was having vulnerability in Gemfile.lock, Gemfile.lock makes our application a single package of both your own code and the third-party code it ran the last time you know for sure that everything worked. Specifying exact versions of the third-party code you depend on in your Gemfile would not provide the same guarantee, because gems usually declare a range of versions for their dependencies.

What were vulnerable dependencies in Gemfile.lock ?

Two dependency namely Nokogiri and Yajl-Ruby were having security vulnerability.

Nokogiri is an HTML, XML, SAX, and Reader parser. Among Nokogiri’s many features is the ability to search documents via XPath or CSS3 selectors whereas

Yajl-Ruby gem is a C binding to the excellent YAJL JSON parsing and generation library. Older versions of both the dependencies were having security vulnerability.

Security alerts for a vulnerable dependency in our repository include a severity level and a link to the affected file in our project. When available, the alerts also include a link to the CVE record and a suggested fix.

What was the suggested fix ?

One way to fix this problem was to update the vulnerable dependencies to latest versions.

The versions of Nokogiri and Yajl-Ruby which were used in SUSPER are:

Nokogiri (~>1.5)

Yajl-Ruby (1.1.0)

What are the best ways to update dependencies without breaking

the project ?

The best way to update a dependency is to check where those dependencies are used in project and what are breaking changes which are introduced within the dependencies.

How vulnerable dependencies were updated ?

Firstly we updated the Bundler the tool we use to update our gems in Gemfile.lock,from version 1.13.6 to 1.16.0.

We then updated Nokogiri dependency and other sub dependencies using  bundle update nokogiri i.e:

mini_portile2 (2.1.0) -> mini_portile2 (2.3.0)

nokogiri (1.6.8.1) ->nokogiri (1.8.2)

Then we checked the project for integrity , and the project was working well.

We then tried to update Yajl-Ruby, but there was a problem in updating Yajl-Ruby,

We later found that Yajl-Ruby was replaced by many other dependencies.

We therefore updated whole Gemfile.lock . Following are two simple steps to update Gemfile.lock

bundle update

bundle install

 

We later checked that whether the new dependencies do not break the current project and we found that there were no breaking changes involved in updated dependencies.

Security alerts for vulnerable dependencies list the affected dependency and, in some cases, use machine learning to suggest a fix from the GitHub community. By default, we receive a weekly email summarizing security alerts for up to 10 of our repositories. We can choose to receive security alerts individually by email, in a daily digest email, in our web notifications, or in the GitHub user interface.

Resources:

Setting up YaCy Grid locally

SUSPER is a search interface that uses P2P search engine YaCy . Search results are displayed using Solr server which is embedded into YaCy. The retrieval of search results is done using YaCy search API. When a search request is made in one of the search templates, an HTTP request is made to YaCy and the response is done in JSON. In this blog post I will show how to setup YaCy Grid locally.

What is YaCy Grid ?

The YaCy Grid is the second-generation implementation of YaCy, a peer-to-peer search engine. The required storage functions of the YaCy Grid are:

  1.  An asset storage, basically a file sharing environment for YaCy components,an ftp server is used for asset storage.
  2.  A message system providing an Enterprise Integration Framework using a message-oriented middleware,RabbitMQ message queues for the message system.
  3.  A database system providing search-engine related retrieval functions.It uses Elasticsearch for database operations.

How to setup YaCy Grid locally ?

YaCy Grid have 4 components MCP(Master Connect Program), Loader, Crawler and  Parser.

  1. Clone all the components using –recursive flag.

git clone --recursive https://github.com/yacy/yacy_grid_mcp.git
git clone --recursive https://github.com/yacy/yacy_grid_parser.git
git clone --recursive https://github.com/yacy/yacy_grid_crawler.git
git clone --recursive https://github.com/yacy/yacy_grid_loader.git
  1.  Now to starting YaCy Grid requires starting Elasticsearch, RabbitMQ with Username `anonymous` and Password `yacy` and an ftp server(it can be omitted as MCP can take over).
  2.  All the above steps can also be done in a single step by running a python script in `bin` folder `run_all.py`
  3.  Working of `run_all.py` in yacy_grid_mcp:

if not checkportopen(9200):
   print "Elasticsearch is not running"
   mkapps()
   elasticversion = 'elasticsearch-5.6.5'
   if not os.path.isfile(path_apphome + '/data/mcp-8100/apps/' + elasticversion + '.tar.gz'):
       print('Downloading ' + elasticversion)
       urllib.urlretrieve ('https://artifacts.elastic.co/downloads/elasticsearch/' + elasticversion + '.tar.gz', path_apphome + '/data/mcp-8100/apps/' + elasticversion + '.tar.gz')
   if not os.path.isdir(path_apphome + '/data/mcp-8100/apps/elasticsearch'):
       print('Decompressing' + elasticversion)
       os.system('tar xfz ' + path_apphome + '/data/mcp-8100/apps/' + elasticversion + '.tar.gz -C ' + path_apphome + '/data/mcp-8100/apps/')
       os.rename(path_apphome + '/data/mcp-8100/apps/' + elasticversion, path_apphome + '/data/mcp-8100/apps/elasticsearch')
   # run elasticsearch
   print('Running Elasticsearch')
   os.chdir(path_apphome + '/data/mcp-8100/apps/elasticsearch/bin')
   os.system('nohup ./elasticsearch &')

 

  • Checks whether Elasticsearch is running or not, if not then runs Elasticsearch.
if checkportopen(15672):
   print "RabbitMQ is Running"
   print "If you have configured it according to YaCy setup press N"
   print "If you have not configured it according to YaCy setup or Do not know what to do press Y"
   n=raw_input()
   if(n=='Y' or n=='y'):
       os.system('service rabbitmq-server stop')
       
if not checkportopen(15672):
   print "rabbitmq is not running"
   os.system('python bin/start_rabbitmq.py')
  • Checks whether RabbitMQ is running or not, if yes then asks user to configure it according to YaCy Grid setup by pressing Y or else ignore,if not then starts RabbitMQ according to required configuration.
subprocess.call('bin/update_all.sh')
  • .Updates all the Grid components including MCP.

if not checkportopen(2121):
   print "ftp server is not Running"
  • Checks for an ftp server and prints message accordingly.

def run_mcp():
   subprocess.call(['gnome-terminal', '-e', "gradle run"])

def run_loader():
   os.system('cd ../yacy_grid_loader')
   subprocess.call(['gnome-terminal', '-e', "gradle run"])

def run_crawler():
   os.system('cd ../yacy_grid_crawler')
   subprocess.call(['gnome-terminal', '-e', "gradle run"])

def run_parser():
   os.system('cd ../yacy_grid_parser')
   subprocess.call(['gnome-terminal', '-e', "gradle run"])

 

  • Runs all components of YaCy Grid in separate terminal.

Once user starts it, then he can start using YaCy Grid through terminal.

If a YaCy Grid service has used the MCP once, it learns from the MCP to connect to the infrastructure itself. For example:

  • a YaCy Grid service starts up and connects to the MCP
  • the Grid service pushes a message to the message queue using the MCP
  • the MCP fulfils the message send operation and response with the actual address of the message broker
  • the YaCy Grid service learns the direct connection information
  • whenever the YaCy Grid service wants to connect to the message broker again, it can do so using a direct broker connection. This process is done transparently, the Grid service does not need to handle such communication details itself. The routing is done automatically. To use the MCP inside other grid components the git submodule functionality is used.

Resources:

Installing Susper Search Engine and Deploying it to Heroku

Susper is a decentralized Search Engine that uses the peer to peer system yacy and Apache Solr to crawl and index search results.

Search results are displayed using the Solr server which is embedded into YaCy. All search results must be provided by a YaCy search server which includes a Solr server with a specialized JSON result writer. When a search request is made in one of the search templates, a HTTP request is made to YaCy. The response is JSON because that can much better be parsed than XML in JavaScript.

In this blog, we will talk about how to install Susper search engine locally and deploying it to Heroku (A cloud application platform).

How to clone the repository

Sign up / Login to GitHub and head over to the Susper repository. Then follow these steps.

  1. Go ahead and fork the repository
https://github.com/fossasia/susper.com

2.   Get the clone of the forked version on your local machine using

git clone https://github.com/<username>/susper.com.git

3. Add upstream to synchronize repository using

git remote add upstream https://github.com/fossasia/susper.com.git

Getting Started

The Susper search application basically consists of the following :

  1. First, we will need to install angular-cli by using the following command:
npm install -g @angular/[email protected]

2. After installing angular-cli we need to install our required node modules, so we will do that by using the following command:

npm install

3. Deploy locally by running this

ng serve

Go to localhost:4200 where the application will be running locally.

How to Deploy Susper Search Engine to Heroku :

  1. We need to install Heroku on our machine. Type the following in your Linux terminal:
wget -O- https://toolbelt.heroku.com/install-ubuntu.sh | sh

This installs the Heroku Toolbelt on your machine to access Heroku from the command line.

  1. Create a Procfile inside root directory and write
web: ng serve
  1. Next, we need to login to our Heroku server (assuming that you have already created an account).

Type the following in the terminal:

heroku login

Enter your credentials and login.

  1. Once logged in we need to create a space on the Heroku server for our application. This is done with the following command
heroku create
  1. Add nodejs buildpack to the app
heroku buildpacks:add –index 1 heroku/nodejs
  1. Then we deploy the code to Heroku.
git push heroku master
git push heroku yourbranch:master # If you are in a different branch other than master

Resources

Auto Deployment of Pull Requests on Susper using Surge Technology

Susper is being improved every day. Following every best practice in the organization, each pull request includes a working demo link of the fix. Currently, the demo link for Susper can be generated by using GitHub pages by running these simple commands – ng build and npm run deploy. Sometimes this process on slow-internet connectivity takes up to 30 mins to generate a working demo link of the fix.

Surge is the technology which publishes or generates the static web-page demo link, which makes it easier for the developer to deploy their web-app. There are a lot of benefits of using surge over generating demo link using GitHub pages:

  • As soon as the pull request passes Travis CI, the deployment link is generated. It has been set up as such, no extra terminal commands will be required.
  • Faster loading compared to deployment link is generated using GitHub pages.

Surge can be used to deploy only static web pages. Static web pages mean websites that contain fixed contents.

To implement the feature of auto-deployment of pull request using surge, one can follow up these steps:

  • Create a pr_deploy.sh file which will be executed during Travis CI testing.
  • The pr_deploy.sh file can be executed after success i.e. when Travis CI passes by using command bash pr_deploy.sh.

The pr_deploy.sh file for Susper looks like this:

#!/usr/bin/env bash
if [ “$TRAVIS_PULL_REQUEST” == “false” ]; then
echo “Not a PR. Skipping surge deployment.”
exit 0
fi
angular build production

npm i -g surge

export SURGE_LOGIN=test@example.co.in
# Token of a dummy account
export SURGE_TOKEN=d1c28a7a75967cc2b4c852cca0d12206

export DEPLOY_DOMAIN=https://pr-${TRAVIS_PULL_REQUEST}-fossasia-susper.surge.sh
surge project ./dist domain $DEPLOY_DOMAIN;

 

Once pr_deploy.sh file has been created, execute the file in the travis.yml by using command bash pr_deploy.sh.

In this way, we have integrated the surge technology for auto-deployment of the pull requests in Susper.

References:

Fixing Infinite Scroll Feature for Susper using Angular

In Susper, we faced a unique problem. Every time the image tab was opened, and the user scrolled through the images, all the other tabs in the search engine, such as All, Videos etc, would stop working. They would continue to display image results as shown:

Since this problem occurred only when the infinite scroll action was called in the image tab, I diagnosed that the problem probably was in the url parameters being set.

The url parameters were set in the onScroll() function as shown:

onScroll () {
let urldata = Object.assign({}, this.searchdata);
this.getPresentPage(1);
this.resultDisplay = ‘images’;
urldata.start = (this.startindex) + urldata.rows;
urldata.fq = ‘url_file_ext_s:(png+OR+jpeg+OR+jpg+OR+gif)’;
urldata.resultDisplay = this.resultDisplay;
urldata.append = true;
urldata.nopagechange = true;
this.store.dispatch(new queryactions.QueryServerAction(urldata));
};

The parameters append and nopagechange were to ensure that the images are displayed in the same page, one after the other.
To solve this bug I first displayed the query call each time a tab is clicked on the web console.
Here I noticed that for the tab videos, nopagechange and append attributes still persisted, and had not been reset. The start offset had not been set to 0 either.
So adding these few lines before making a query call from any tab, would solve the problem.

urldata.start = 0;
urldata.nopagechange = false;
urldata.append = false;

Now the object is displayed as follows:

Now videos are displayed in the videos tab, text in the text tab and so on.
Please refer to results.component.ts for the entire code.

References:

  1. On how to dispatch queries to the store: https://gist.github.com/btroncone/a6e4347326749f938510
  2. Tutorial on the ngrx suite:http://bodiddlie.github.io/ng-2-toh-with-ngrx-suite/

Creating an Infobox for Mobile View Using Angular in Susper

In Susper, the Information and Analytics boxes disappeared for widths smaller than 1100px, since they were too big to fit in the existing page layout.
In Laptop view:

In Mobile view:

Hence we decided to design a new Info-cum-Analytics box for mobile devices, where the Analytics are displayed only if the ‘Show Analytics’ button is clicked and they are hidden on clicking the ‘Hide Analytics’ button.

The following is the html code for the Infobox:

<div class=“combo-box”>
<appinfobox></app-infobox>
<button class=“btn” id=“toggle-button” (click)=“BoxToggle()” type=“button” datatoggle=“collapse” datatarget=“#statbox” ariaexpanded=“false” ariacontrols=“collapseExample”>
{{boxMessage}} Analytics
</button>
<appstatsbox class=“collapse” id=“statbox”></app-statsbox>
</div>

We can make the following observations:

  • The combo box has both the Info and the Stats box, in addition to a button, which toggles the display status of the Analytics box.
  • To display the appropriate message on the button, we have a special function called BoxToggle() which sets the value of boxMessage()
  • The data-toggle attribute of the button has been set to collapse and the data-target is statbox
  • The statsbox has the class collapse, so that it collapses on clicking on the Toggle button.

The typescript code is as follows:

BoxToggle() {
if (this.boxMessage === ‘Show’) {
this.boxMessage = ‘Hide’;
} else {
this.boxMessage = ‘Show’;
}
}

This code thus effectively toggles between the messages Show and Hide, if the button is clicked when the message says Show, it changes to Hide and vice-versa

The CSS code:

@media screen and (min-width:768px) {
div.combo-box {
display: none;
}
}

The css code ensures that this combo-box is displayed only for widths less than 768px (only mobile and tablet screens).

To view the entire code you can check results.component.html and results.component.css in the Susper repository.

References

  1. W3 Schools for Collapsible buttons and their implementation: https://www.w3schools.com/bootstrap/bootstrap_collapse.asp
  2. Official Bootstrap documentation for collapsible buttons: https://v4-alpha.getbootstrap.com/components/collapse/

Implementation of Image Viewer in Susper

We have implemented image viewer in Susper similar to Google.

Before when a user clicks on a thumbnail the images are opened in a separate page, but we want to replace this with an image viewer similar to Google.

Implementation Logic:

1. Thumbnails for images in susper are arranged as shown in the above picture.

2. When a user clicks on an image a hidden empty div(image viewer) of the last image in a row is opened.

3. The clicked image is then rendered in the image viewer (hidden div of the last element in a row).

4. Again clicking on the same image closes the opened image viewer.

5. If a second image is clicked then, if an image is in the same row, it is rendered inside the same image viewer. else if the image is in another row, this closes the previous image viewer and renders the image in a new image viewer (hidden div of the last element of the row)

6. Since image viewer is strictly the hidden empty div of the last element in a row when it is expanded it occupies the position of the next row, moving them further down similar to what we want.

Implementation Code

results.component.html

<div *ngFor="let item of items;let i = index">
 <div class="item">
   <img src="{{item.link}}" height="200px" (click)="expandImage(i)" [ngClass]="'image'+i">
 </div>
 <div class=" item image-viewer" *ngIf="expand && expandedrow === i">
   <span class="helper"></span> <img [src]="items[expandedkey].link" height="200px" style="vertical-align: middle;">
 </div>

</div>

Each thumbnail image will have a <div class=” item image-viewer” which is in hidden state initially.

Whenever a user clicks on a thumbnail that triggers expandImage(i)

results.component.ts

expandImage(key) {
 if (key === this.expandedkey    this.expand === false) {
   this.expand = !this.expand;
 }
 this.expandedkey = key;
 let i = key;
 let previouselementleft = 0;
 while ( $('.image' + i) && $('.image' + i).offset().left > previouselementleft) {
   this.expandedrow = i;
   previouselementleft = $('.image' + i).offset().left;
   i = i + 1;

The expandImage() function takes the unique key and finds which image is the last element is the last image in the whole row, and on finding the last image, expands the image viewer of the last element and renders the selected image in the image viewer.

The source code for the whole implementation of image viewer could be seen at pull: https://github.com/fossasia/susper.com/pull/687/files

Resources:

  1. Selecting elements in Jquery: https://learn.jquery.com/using-jquery-core/selecting-elements/

 

Creating A Better Responsive Design In Susper

A lot of work has been done on making Susper, a wonderful search-engine and still more work have to be done on it. To become a good competitor in the market, one should make their website UI design such that:

  • It should be eye-catching for the users on the first-time visit to the website.
  • It should be easy to use with simple UI features rather than having more complex UI features.

We have been more oriented towards the material design. We have used Bootstrap technology for designing UI. Earlier, we proposed an idea of creating a UI using Angular Material v2 but it was dropped due to time limitations and other issue priorities.

To make Susper a better competitor in the market, we made sure it should be responsive as well on the following devices:

  • Mobile screen devices:
    • 320px – Smaller screen size.
    • 375px – Medium screen size.
    • 425px – Larger screen size.
  • Tablets:
    • 768px – default screen size for tablets.
  • Laptops:
    • 1024px – Smaller screen size.
    • 1440px – Larger screen size.
  • 4K:
    • 2560px – Default screen size.

We targeted these devices using @media queries in CSS3. For e.g. if I want to make a site responsive for the mobile devices, I will be using:

@media screen and (minwidth: 320px) and (maxwidth: 425px) {
  // do something
}

 

Here, min-width: 320px means that the screen size should be greater than and equal to 320px and max-width: 425px means that the screen size should be less than and equal to 425px.

It is not necessary to use only these dimensions. Suppose if there is break in UI design between 320px and 425px then, one can add that screen size using @media query. In this case, nested @media queries play a quite good role.

@media screen and (minwidth: 320px) and (maxwidth: 425px) {
  // do something
  // let’s say, break in UI design is observed at 375px
  // add nested @media query
  @media screen and (minwidth: 375px) {
    // do something
  }
}

 

We’re still improving our CSS code at present following this grid pattern. One can check UI code at Susper repository hosted on GitHub: https://github.com/fossasia/susper.com

We have also used a lot of breakpoints which are not nested. But it’s good practice to break points in nested form. This will be solved while improving our CSS code.

Here are some screenshots of the current responsiveness of Susper:

  • Mobile screen devices:
  • Tablet devices:

  • Laptops:
  • 4K display:

Resources:

Creating A Dockerfile For Yacy Grid MCP

The YaCy Grid is the second-generation implementation of YaCy, a peer-to-peer search engine. A YaCy Grid installation consists of a set of micro-services which communicate with each other using a common infrastructure for data persistence. The task was to deploy the second-generation of YaCy Grid. To do so, we first had created a Dockerfile. This dockerfile should start the micro services such as rabbitmq, Apache ftp and elasticsearch in one docker instance along with MCP. The microservices perform following tasks:

  • Apache ftp server for asset storage.
  • RabbitMQ message queues for the message system.
  • Elasticsearch for database operations.

To launch these microservices using Dockerfile, we referred to following documentations regarding running these services locally: https://github.com/yacy/yacy_grid_mcp/blob/master/README.md

For creating a Dockerfile we proceeded as follows:

FROM ubuntu:latest
MAINTAINER Harshit Prasad# Update
RUN apt-get update
RUN apt-get upgrade -y# add packages
# install jdk package for java
RUN apt-get install -y git openjdk-8-jdk

#install gradle required for build
RUN apt-get update && apt-get install -y software-properties-common
RUN add-apt-repository ppa:cwchien/gradle
RUN apt-get update
RUN apt-get install -y wget
RUN wget https://services.gradle.org/distributions/gradle-3.4.1-bin.zip
RUN mkdir /opt/gradle
RUN apt-get install -y unzip
RUN unzip -d /opt/gradle gradle-3.4.1-bin.zip
RUN PATH=$PATH:/opt/gradle/gradle-3.4.1/bin
ENV GRADLE_HOME=/opt/gradle/gradle-3.4.1
ENV PATH=$PATH:$GRADLE_HOME/bin
RUN gradle -v

# install apache ftp server 1.1.0
RUN wget http://www-eu.apache.org/dist/mina/ftpserver/1.1.0/dist/apache-ftpserver-1.1.0.tar.gz
RUN tar xfz apache-ftpserver-1.1.0.tar.gz

# install RabbitMQ server
RUN wget https://www.rabbitmq.com/releases/rabbitmq-server/v3.6.6/rabbitmq-server-generic-unix-3.6.6.tar.xz
RUN tar xf rabbitmq-server-generic-unix-3.6.6.tar.xz

# install erlang language for RabbitMQ
RUN apt-get install -y erlang

# install elasticsearch
RUN wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.5.0.tar.gz
RUN sha1sum elasticsearch-5.5.0.tar.gz
RUN tar -xzf elasticsearch-5.5.0.tar.gz

# clone yacy_grid_mcp repository
RUN git clone https://github.com/nikhilrayaprolu/yacy_grid_mcp.git
WORKDIR /yacy_grid_mcp

RUN cat docker/configftp.properties > ../apacheftpserver1.1.0/res/conf/users.properties

# compile
RUN gradle build
RUN mkdir data/mcp-8100/conf/ -p
RUN cp docker/config-mcp.properties data/mcp-8100/conf/config.properties
RUN chmod +x ./docker/start.sh

# Expose web interface ports
# 2121: ftp, a FTP server to be used for mass data / file storage
# 5672: rabbitmq, a rabbitmq message queue server to be used for global messages, queues and stacks
# 9300: elastic, an elasticsearch server or main cluster address for global database storage
EXPOSE 2121 5672 9300 9200 15672 8100

# Define default command.
ENTRYPOINT [“/bin/bash”, “./docker/start.sh”]

 

We have created a start.sh file to start RabbitMQ and Apache FTP services. At the end, for compilation gradle run will be executed.

adduser –disabled-password –gecos ” r
adduser r sudo
echo ‘%sudo ALL=(ALL) NOPASSWD:ALL’ >> /etc/sudoers
chmod a+rwx /elasticsearch-5.5.0 -R
su -m r -c ‘/elasticsearch-5.5.0/bin/elasticsearch -Ecluster.name=yacygrid &’
cd /apacheftpserver1.1.0
./bin/ftpd.sh res/conf/ftpdtypical.xml &
/rabbitmq_server-3.6.6/sbin/rabbitmq-server -detached
sleep 5s;
/rabbitmq_server-3.6.6/sbin/rabbitmq-plugins enable rabbitmq_management
/rabbitmq_server3.6.6/sbin/rabbitmqctl add_user yacygrid password4account
echo [{rabbit, [{loopback_users, []}]}]. >> /rabbitmq_server-3.6.6/etc/rabbitmq/rabbitmq.config
/rabbitmq_server-3.6.6/sbin/rabbitmqctl set_permissions -p / yacygrid “.*” “.*” “.*”
cd /yacy_grid_mcp
sleep 5s;
gradle run

 

start.sh will first add username and then password. Then it will start RabbitMQ along with Apache FTP.  For username and password, we have created a separate files to configure their properties during Docker run which can be found here:

The logic behind running all the microservices in one docker instance was: creating each container for microservice and then link those containers with the help of docker-compose.yml file.

The Dockerfile which we have created was corresponding to one image. Another image was elasticsearch which was linked to this Dockerfile. The latest version of elasticsearch image was already available on their site: https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html

We configured the docker-compose.yml file according to the reference link provided above. The docker-compose file can be found here: https://github.com/yacy/yacy_grid_mcp/blob/master/docker/docker-compose.yml

The source code for the implementation of whole structure can be found here: https://github.com/yacy/yacy_grid_mcp/tree/master/docker

Resources