Understand Cluster
For each cluster, we offer a detailed summary page where you can view the key statistics and quliaty metrics of a cluster.
Key cluster statistics
At the top of the cluster details page, you will see the consensus peptide of the cluster and cluster related statistics. The key statistics include:
- #Spectra: Number of spectra identifing the consensus peptide / Total number of spectra in the cluster.
- #Projects: Number of projects identifing the consensus peptide / Total number of projects in the cluster.
- #Species: Number of species assigned to the consensus peptide / Total number of species in the cluster.
Species chart
This chart shows the percentage of species within a cluster. It is important to note that the numbers shown are the number of PSMs. These numbers are likely to be larger than the number of spectra within a cluster.
Modification chart
This chart shows the percentage of modifications within a cluster. It is important to note that the numbers shown are the number of PSMs. These numbers are likely to be larger than the number of spectra within a cluster.
Unique peptides
This table show the number of unique peptide seqences with their modifiations. We also display the following metrics:
- #PSM: the number of PSMs identify the peptide.
- #Species: the number of species identify the peptide.
- #Projects: the number of original projects identify the peptide.
Original projects
The details of the original projects from the PRIDE Archive within the cluster. You can flip thought the projects using the arrows on either side of the panel.
Consensus spectrum
The peak list shows the peaks of the consensus spectrum of the cluster using Lorikeet Spectrum Viewer
Precursor m/z chart
This boxplot shows the delta m/z distribution for the PSMs identify the consensus peptide and the whole of the cluster.
Delta m/z is the different between the experimental m/z and the theoritical m/z. As a rule of thumb, the smaller of delta m/z the better.


Introduction
The goal of the quiz is to introduce students to the most significant and exciting computing paradigm--net-centric
computing and service-oriented computing, which is expected to have an impact on all aspects of software
construction at least as wide as that of object-oriented paradigm. This is a wide aea and it is ever-evolving. We focus
on the following aspects.
Click on the Start Button to begin.


JSON attribute name(s) for identifying objects (delimited by commas):
Objects will be merged by the values of one of these attributes (which is found first).
Attributes to merge and their aggregations (delimited by commas):
Aggregations are used when more than one attribute value is specified for the same id.
Possible aggregations are: oneof, sum, concat, asArray, asSet, count.
Aggregations are specified using a colon and the aggregation name, which follow the attribute name, e.g. attr1:oneof,attr2:sum,attr3,attr4:asSet,newAttr:count
Default is oneof (used for attr3 from above).


First Blog
Navigation
Having just started Enspiral Dev Academy's (EDA) phase 0, I am very
excited to polish my skills as a web developer. I think EDA offers a
very balanced perspective to working in the technology industry. I
am particularly interested in doing things related to mindfulness and
keeping a balanced work and private life.
I would like to fully engage with my team-mates during my time with
EDA. I have prior programming experience from my Bachelors of Science
with computer science and I tutored students after my first year
studying. Because of this I'm ready to help my cohort and I'm ready
learn from them because everyone is good at different things.
I chose to study with EDA because of the way they marketed themselves
- as a course that teaches up to date web development skills, with a
very high graduate employment rate in the field. I still expect this
from them, and I am ready to commit myself to this. I have been especially
impressed talking to the EDA staff about their positions on student
learning (highly collaborative) and social equality and diversity.
Working in the highly collaborative environment of EDA makes me nervous as
I will have to rely on others to produce work, and I have been disappointed
by others in the past. However, each new team deserves to be trusted
until proven otherwise and I'm looking to learn a lot from my new
team-mates as well as other skills like leadership and planning.


Make an excel file which has the given headings. Do not interchange the column of headings. The headings can be spelled the way you are comfortable with.
Step 2
{% load static %}
Add the necessary data. Drag and drop or click choose button to add file. Then click submit.
Step 3
{% load static %}
Drag and drop or click choose file button to add files.


Is it possible to send a bunch of data to Emoncms in one update or burst, aka only do one update every hour or 6 hours? Like a cronjob that updates Emoncms instead of immediate updates?
Use Case:
I have a vacation home on a lake that does not have an internet connection. I do have a USB mobile hotspot that I can use with a Raspberry Pi. What I would like to do is schedule it to connect every 6 - 12 hours, and push all of the updates during that period.
Purpose:
The house is in a cold region, and does have water / power on at all times. I would like to monitor energy consumption over time, as well as alert on any major temperature shifts.
Setup:
- EnomPi Base Station
- emonTx V3
- (2) emonTh V1.4
Re: Scheduled upload to Emoncms.org?
That's quite an uncommon request so there isn't really a pre-prepared solution, although I personally think there would be a demand for it.
I have helped a couple of other users with similar "no network" scenarios but they have opted for collecting data locally and then manually fetching the data using a data-stick or similar, there have also been users that have used a mobile data dongle to post minimised data but at a more frequent interval, so there will be some useful info buried in the forum if you search,
Something that may help you achieve what you want is the buffering feature in emonhub, the buffer size is user defined as is the send interval. My own emonhub will buffer up to a weeks data, and a 24hr buffer wouldn't make a dent in the 1gb ram of the emonPi. using this buffering feature you could tackle this relatively easily in 1 of 2 ways, niether is over elegant but both would do the job. 1) leave your dongle connected (accessible) 24/7 and prevent anything else using it, set emonhub to send every n hours (set in seconds ie 7200 = 2hrs) or 2) just let emonhub keep trying to send every n secs/mins but only allow access to the dongle at certain times so emonhub would just keep accumulating data until the connection is successful and then offloads the buffers.
I have suggested this method to another user that had a remote greenhouse with a monitor, he visited the greenhouse daily so it was possible to configure emonhub to accumulate data and whenever he visited he just needed to have tethering active on his mobile phone and the Pi could connect by wifi and off load the buffered data.
however you say you have an emonPi, so I assume you are using the "emonPi image" which includes a "emon-pi" variant of emonhub and the buffering has been removed from that version so you would need to look at a custom image to use buffering.
So it should totally be possible if you're happy to roll your sleeves up and make some changes etc.
There has also been discussions on using CoAP or MQTT as the overheads are so much smaller allowing more frequent posting without massive data use, this would probally serve better if using the data for alerts.
Paul


5 things we learned about Whitney Houston and Robyn Crawford's relationship from her tell-all 'Red Table Talk'
by Bashiera Parker (@bparks_)Cape Town – With the release of her tell-all book, A Song For You, Robyn Crawford appeared on Red Table Talk on Monday detailing her decades-long relationship with Whitney Houston.
"We were intimate on all levels," she revealed to Jada Pinkett Smith in the interview that left the Set It Off actress in tears.
Robyn spoke about her relationship with Whitney, her altercations with Bobby and the reason she needed to distance herself from the I Will Always Love You singer.
Here are five things we learned from her explosive interview on Red Table Talk:
1. WHITNEY AND ROBYN HAD A PHYSICAL RELATIONSHIP
Whitney was 16 when she met Robyn who was 19 years old at the time at their community centre. They were both summer counsellors. They were immediately drawn to one another and soon entered into a sexual relationship that lasted two years.
"One day after camp, we were just talking. There was something there that made us connect. One moment we both just met, and, you know, our mouths touched, and that was our first kiss," she recalled. "It was awesome."
2. WHITNEY'S MOM DIDN'T APPROVE
Robyn says she and Whitney weren't hiding their love, but Whitney's mom didn't approve. Inevitably, Whitney ended her physical relationship with Robyn which she explained may have had to do with her mom's "bitterness" towards her, but also Whitney's choice to put her career first in a time that wasn't as accepting of their relationship.
3.  "She was worth that," Robyn said, so she stuck around, even if she could only appear to be a close friend of the singer.
"A lot of people have a difficult time understanding relationships of that manner because everybody thinks the highest grade of relating is romantic and sexual and it's not," Jada added. "Those components aren't everything."
4. ROBYN STOOD BY WHITNEY THROUGHOUT HER TUMULTUOUS RELATIONSHIP WITH BOBBY BROWN
Whitney Houston went on to marry R&B singer and rapper Bobby Brown in 1992. Though their relationship was rocky – Robyn recalls many moments in which she confronted Whitney about gashes on her face and fights that erupted in front of her – she stood by the singer as her maid of honour when the couple said I do, and throughout their tumultuous marriage to one another.
"If that's what she wanted, I would be there for her," Robyn said. "I wanted Whitney to have the life that she wanted for herself. I hoped that it would be fruitful for her."
Still, Robyn felt she should support her friend who loved this man despite hearing about his reputation. "It's not right to stand by someone and then think ill of it," she said.
5. ROBYN DIDN'T LEAVE BECAUSE OF BOBBY
Robyn didn't always get along with Bobby – there were numerous reports about the two and their disagreements – but he wasn't the reason she decided to walk away.
"I was at a breaking point," Robyn explained and said it was everything going on and the stress of it all that made her leave. "The situation was out of control," she continued, "I felt like I'd done all that I could do."
Robyn goes on to say that she didn't really think she was saying goodbye to Whitney until she received a phone call from her in which she heard her say her name – just her name – but in a way that certainly got her attention.
"I feel like I was waiting for her, and she was waiting for me," Robyn says of their time apart, "Time just don't wait."
WATCH THE FULL INTERVIEW HERE:
READ NEXT: Bobby Brown tells us: 'God is still working on me'


{# Includable snippet to display quality review status information for a tagged trait. #} {% comment %} Usage: {% include 'tags/_taggedtrait_quality_review_panel.html' %} Required context variables: show_dcc_review_add_button show_dcc_review_update_button show_dcc_review_confirmed show_dcc_review_needs_followup show_study_response_status show_study_agrees show_study_disagrees show_dcc_decision show_dcc_decision_add_button show_dcc_decision_update_button show_decision_remove show_decision_confirm show_decision_comment show_delete_button show_archived quality_review_panel_color {% endcomment %}
This tagged variable has been confirmed by the DCC.
{% elif show_dcc_review_needs_followup %}
This tagged variable has been flagged for removal by the DCC for the following reason:
{{ tagged_trait.dcc_review.comment }}
This tagged variable has not yet been reviewed by the DCC.
{% endif %} {% if show_study_response_status %} {% if show_study_disagrees %}
The study has indicated that this variable should remain tagged for the following reason:
{{ tagged_trait.dcc_review.study_response.comment }}
The study has agreed that this tagged variable should be removed.
{% endif %} {% endif %} {% if show_dcc_decision %}
{% if show_decision_confirm %}
This tagged variable has been confirmed by the DCC.
{% endif %}
{% if show_decision_remove %}
This tagged variable will be removed by the DCC.
{% endif %}
{% if show_decision_comment %}
{{ tagged_trait.dcc_review.dcc_decision.comment }}
{% endif %}
This tagged variable has been removed by the DCC.
{% endif %} {% if show_dcc_review_add_button %} Add DCC review for this tagged variable {% endif %} {% if show_dcc_review_update_button %} Update DCC review for this tagged variable {% endif %} {% if show_dcc_decision_add_button %} Make final decision for this tagged variable {% endif %} {% if show_dcc_decision_update_button %} Update final decision for this tagged variable {% endif %}


[OgreKit Lua] |
Methods
| Sensor.getFrequency | |
| Sensor.getPulse | |
| Sensor.isLevel | |
| Sensor.isTap | |
| Sensor.isInverse | |
| Sensor.setFrequency | |
| Sensor.setInvert | |
| Sensor.setLevel | |
| Sensor.setPulse | |
| Sensor.setTap |
Returns the frequency.
Returns the pulse mode.
Returns true if this sensor is a state transition detector.
Returns true if this sensor is in tap / toggle mode.
Returns true if this sensor is inverting events.
Sets the update frequency in game ticks (1 / 60).
| number | The update rate in game ticks. |
Makes this sensor invert event queries.
| val | bool value. |
Makes this sensor execute on state changes.
| val | bool value. |
Sets the pulse mode for this sensor.
| pulse | PulseMode |
Enable tap / toggle mode.
| val | Boolean value. |


I first started using a variety of cognition-enhancing substances (nootropics) when I had a lot of work/study-related pressures and felt it difficult to cope with these pressures. Of all the chemical ones (rather than proper nutritional supplements such as choline, ginkgo biloba, or L-Glutamine) I had tried, this seems to be the one that works best for me, and with the least side effects.
As I had to perform quite well and be quite alert from very early in the morning at times but am not really a 'morning person' I decided to try taking it within half an hour of waking and to see where the effects took me. And I think it was more than placebo, because it repeatedly did the same job for me:
ONE (1) 300mg pill would consistently make me feel considerably more alert, confident, talkative and eager to perform in front of a large audience within about half an hour of taking it. This effect would last for about four or five hours before dropping off to baseline. When taken with a good strong cup of coffee (and perhaps with some choline etc.) the effect could be slightly stronger than is comfortable if I wanted to be matter-of-fact and fully in control rather than over-the-top and a little bit jittery.
Taken in the evening, it helped boost energy for a few more hours, not unlike a taurine/b-vit/caffeine based energy drink but without the restless jitters (which are probably due to caffeine, to which I am quite sensitive).
I have, on one occasion, used 5 pills (5 x 300mg) taken within a timespan of about 3 hours, along with some kava kava and plenty of alcohol, in order to enhance a clubbing/party night. With this I had plenty of energy without any jitters like you would with, say, amphetamines.
I did, however, get several of the mental effects of speed/meth, including increased confidence, slightly compulsive and/or reckless thoughts and a tendency to talk gibberish.
After my return home, by the end of the night, I found it hard to fall asleep and felt some odd pressure developing in my head. Upon waking the next day, I found myself a bit headachy for a few hours, but I cannot say for sure whether this was alcohol/dehydration related or to do with lack of sleep or indeed connected to the adrafinil - probably a combination of all of these factors. Would I do it again just to party? Probably . . . I'd try to drink less but then this is difficult while you feel invincible and reckless.
I have since used it again on a couple of occasions just to eliminate tiredness with a one 300mg dose, once daytime to get some work done and once nighttime to be sociable. Both times it had the desired efects - subtle, comfortable, but definitely there.
I can see myself using this more often in the future when the need arises due to a demanding lifestyle. But I wouldn't do it daily, because I do have feeling that even after just one dose taken on a particular day, I tend to feel a little bit more tired the next day, even if I did get adequate sleep the night before.
It is available online from at least one nootropics vendor outside of the US, which makes it a very affordable little supplement to have on hand. ;)
Experience Reports are the writings and opinions of the individual authors who submit them.
Some of the activities described are dangerous and/or illegal and none are recommended by Erowid.


Research
Ongoing research:
- Can we identify a particular type of activated carbon and soap/shampoo combination that is the most effective at removing particulates ***Almost complete***.
- What is the typical heat loss in a normal shower from shower head to the drain aka how much heating is needed ***Almost complete***.
- Quality control of source components.
- Life Cycle Assessment and scoping the needed certifications for products.
- Making a PCB for our smart shower system
- Testing 12 V DC ballasts with the UV lamps to allow for a fully battery/renewable powered Showerloop
Collaborators and help welcome! Contact us.
ShowerMagic: Super delux recycly showerThe original thesis that started it all. The document includes all the experiments conducted to test water quality before and after going through the filters as well as previous research and future recommendations - which is still the direction of Showerloop today.
Eduard Kobak Testing Facility For A Water Recycling ShowerDuring the making of the ShowerMagic thesis Eduard joined the team to develop an automation system and a software platform whereby the speed of individual experiments could be increased dramatically. Later the same idea was simplified to run on an Arduino micro controller instead of a PC.
Testing the Efficacy of the Prototype Water Purification System for Shower WaterIn 2014 Anup Mishra joined the Showerloop team for a work placement and in 2015 completed his Bachelors thesis. The topic was the final piece of the shower puzzle - can we remove sufficient amounts of soap from the water each cycle. The results were very promising but more research is required because the ingredients in soap are so varied.
Home Water Test Kit ExperimentsIn 2015-2016 Nishan Khadka and Gaurab K.C. joined the team for their work placements. Having twice the responsibility it was tough at first but the guys showed lots of enthusiasm and after some coaching met the goals laid out in the beginning.
One of the objectives was to test simple and inexpensive water quality sensors available from Ebay. While much much more experimenting and quality control should be performed to verify the results (replicate them with multiple sensors) we did get a pretty clear picture of what sensors proved reliable, especially when comparing the cost of these kits to professional chemical lab equipment.
"According to the analysis, pH and conductivity meter are the most reliable test kit and can be used for home test use. TDS meter was not reliable as it showed half of the actual value. Test strips for hardness showed reliable values as the true value of hardness in the sample water was on the range of test strips.
Test strips used for sulphate test had large range (0-250 mg/l). When comparing test strips with Hach Lange, it showed the reliable range while nitrate test strips also showed the reliable range. For E. coli and Coliform confirmation, the test sample also showed the reliable output. Test sample was able to detect at least two coliform colonies.
As a whole, pH meter, conductivity meter, test strips for hardness, sulphate and nitrate and, Coliform and E. coli test powders are reliable for testing home base shower water. However, it is important to notice the range and limitations of test kits before using it."
Testing UV Irradiation Reactors (UV-Lamps) from a SupplierTesting our suppliers 25 W UV lamps for effectiveness to deactivate e.coli bacteria, an indicator organism commonly used to test water quality.


Unofficial Premiere Pro CC API
Help
Remember: Items in the QE API are only available after a call to app.enableQE()


| plusclass {bit64} | R Documentation |
Maintaining integer64 S3 class attribute.
plusclass(class, whichclass) minusclass(class, whichclass)
class |
NULL or a character vector of class attributes |
whichclass |
the (single) class name to add or remove from the class vector |
NULL or a character vector of class attributes
Jens Oehlschlägel<EMAIL_ADDRESS>plusclass("inheritingclass","integer64") minusclass(c("inheritingclass","integer64"), "integer64")


Tight pants next level keffiyeh you probably haven't heard of them. Photo booth beard raw denim letterpress vegan messenger bag stumptown. Farm-to-table seitan, mcsweeney's fixie sustainable quinoa 8-bit american apparel have a terry richardson vinyl chambray. Beard stumptown, cardigans banh mi lomo thundercats. Tofu biodiesel williamsburg marfa, four loko mcsweeney's cleanse vegan chambray. A really ironic artisan whatever keytar , scenester farm-to-table banksy Austin twitter handle freegan cred raw denim single-origin coffee viral.
- $('#example').tooltip(options)
| 名称 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| animation | boolean | true | apply a css fade transition to the tooltip |
| html | boolean | false | Insert html into the tooltip. If false, jquery's text method will be used to insert content into the dom. Use text if you're worried about XSS attacks. |
| placement | string | function | 'top' | how to position the tooltip - top | bottom | left | right |
| selector | string | false | If a selector is provided, tooltip objects will be delegated to the specified targets. |
| title | string | function | '' | default title value if `title` tag isn't present |
| trigger | string | 'hover focus' | how tooltip is triggered - click | hover | focus | manual. Note you case pass trigger mutliple, space seperated, trigger types. |
| delay | number | object | 0 | delay showing and hiding the tooltip (ms) - does not apply to manual trigger type If a number is supplied, delay is applied to both hide/show Object structure is: |
| container | string | false | false | Appends the tooltip to a specific element |
- hover over me
- hover over me</a
- $('#element').tooltip('show')
- $('#element').tooltip('hide')
- $('#element').tooltip('toggle')
- $('#element').tooltip('destroy')
本网站所列开源项目的中文版文档全部由Sunivo Web成员翻译整理
Powered by Sunivo Web Team 站长统计


| Prime Number |
{{element.primeNumber}} |
Name of Business Segment |
{{element.businessSegment}} |
Region |
{{element.region}} |
Branch Code |
{{element.branchCode}} |
Branch Name |
{{element.branchName}} |
Name Of Borrower |
{{element.nameOfBorrower}} |
RM Credit Hub |
{{element.rMCreditHub}} |
Limit |
{{element.limit}} |
Frequency |
{{element.frequency}} |
March Conducted By |
{{element.marchConductedBy}} |
March Conducted Date |
{{element.marchConductedDate|date:'dd-MMM-yyyy'}} |
June Conducted By |
{{element.juneConductedBy}} |
June Conducted Date |
{{element.juneConductedDate|date:'dd-MMM-yyyy'}} |
Sep Conducted By |
{{element.sepConductedBy}} |
Sep Conducted Date |
{{element.sepConductedDate|date:'dd-MMM-yyyy'}} |
Dec Conducted By |
{{element.decConductedBy}} |
Dec Conducted Date |
{{element.decConductedDate |date:'dd-MMM-yyyy'}} |
|
|
|
|


Mute sound
Play the crazy piano tetris theme* (you will need to last 2'45 !). * Credits to Sheet Music Boss


about total managment?
Total Managment is a free , tableless, WC3-compliant web design layout by Template World. This template has been tested and proven compatable with all major browser environments and operating systems. You are free to modify the design to suit your tastes in any way you like.We only ask you to not remove "Design by Template World" and the link http://www.templateworld.comfrom the footer of the template.
If you are interested in seeing more of our web template designs feel free to visit our website, Template World. We intend to add atleast 25 new free templates in the coming month.


CENTER FOR DEVELOPMENT OF ADVANCED COMPUTING
DAC Electronic city, Bangalore
Module Name : Operating System
Assignments
| Assignment No. |
Module Name |
Assignment Topic |
Download Link |
| Assignment 1 |
Operating System |
Linux |
Download assignment 1 file |
Programs examples
| Programs examples files |
Module Name |
Assignment Topic |
Download Link |
| Examples 1 |
Operating System |
Linux |
Download examples 1 file |
Lists of commands
| Lists of commands |
Module Name |
Assignment Topic |
Download Link |
| List 1 |
Operating System |
Linux |
Download commands 1 file |


Syntax:
symbol-plist symbol => plist
(setf (symbol-plist symbol) new-plist)
Arguments and Values:
symbol---a symbol.
plist, new-plist---a property list.
Description:
Accesses the property list of symbol.
Examples:
(setq sym (gensym)) => #:G9723 (symbol-plist sym) => () (setf (get sym 'prop1) 'val1) => VAL1 (symbol-plist sym) => (PROP1 VAL1) (setf (get sym 'prop2) 'val2) => VAL2 (symbol-plist sym) => (PROP2 VAL2 PROP1 VAL1) (setf (symbol-plist sym) (list 'prop3 'val3)) => (PROP3 VAL3) (symbol-plist sym) => (PROP3 VAL3)
Side Effects: None.
Affected By: None.
Exceptional Situations:
Should signal an error of type type-error if symbol is not a symbol.
See Also:
Notes:
The use of setf should be avoided, since a symbol's property list is a global resource that can contain information established and depended upon by unrelated programs in the same Lisp image.


About Sam
Hi I am Sam. I am a systems architect and software developer mostly working in Objective-C, C, Ruby and Scala. In the past I also was known to hang around PHP too. Most of my time is spent living and working in Chicago, Illinois.  I originate from London, currently enjoying a stint at being a legal alien in America’s Midwest.
My real passion is developing system architectures for large scale applications. I am also a very passionate mobile experience developer for iOS (both iPhone and iPad). I spend a lot of time messing around with various personal projects that rarely see the light of day. I am a strong advocate of the Open Source movement and a less frequent core contributor to the Kohana Framework.
Currently I look after technology at Sittercity as Vice President. Previously I have worked with many international clients of all sizes from the BBC to Comic Relief, The Open University to the Joint Information Strategic Committee.

Warning: I can talk endlessly about snowboarding given half a chance!
Contact me
You can contact me through a number of channels listed below.
- Twitter samsoir
- Google+ defreyssinet
- Github samsoir
- LinkedIn Sam de Freyssinet
- Email sam (at this domain)
- Google Talk sam (at this domain)
- Skype samdefreyssinet
- Facetime sam (at this domain)
- iMessage sam (at this domain)
About this blog
This blog is built using Octopress and hosted on Heroku.
All content on this blog is written by Sam de Freyssinet unless otherwise stated. You are free to use, reproduce or publish content from this blog as long as you attribute the source.


Control your Ableton Push 2 with JavaScript!
This project implements the functions described in the Ableton Push 2 MIDI And Display Interface Manual.
See the command list in roadmap.md for a list of functions and an indication
of if they have been implemented yet or not.
Auto-generated API documentation (thanks to typedoc.)
See midi.md for documentation on access to low-level MIDI messages.
$ npm install ableton-push2
var ableton = require('ableton-push2');
// port can be 'user' (default) or 'live'
var push2 = new ableton.Push2(port='user'); // Boom! A New Ableton Push 2!!
push2.monitor(); // Monitor and parse MIDI messages, printing them to console.log
push2.stopMonitor(); // Stops printing Push2 midi messages to console.
// First argument can be either a key name from push2keymap
// or it can also be an array containing [track,scene] with values [[1-8],[1-8]]
// paletteIdx: color palette index [1-127]
push2.setColor([2,3],30); // Set track 2, scene 3 to color 30
push2.setColor("play",127); // Set play key to color 127 (red)
push2.setColor("record",17); // Set record key to color 17 (blueish)
push2.setColor("1/16t",70); // Set 1/16t button to color 70
TODO: Update and document interface for addressing buttons.
push2.getTouchStripConfiguration()
.then((resp)=>{
console.log(resp);
}).catch(console.error);
// Output:
// TouchStripConfiguration {
// LEDsControlledByHost: 0,
// hostSendsSysex: 0,
// valuesSentAsModWheel: 0,
// LEDsShowPoint: 1,
// barStartsAtCenter: 1,
// doAutoReturn: 1,
// autoReturnToCenter: 1 }
Returns a Promise which resolves after verifying that the value was set correctly.
Set all options at once:
var newConfig = {
'LEDsControlledByPushOrHost':1,
'hostSendsSysex': 0,
'valuesSentAsModWheel': 0,
'LEDsShowPoint': 1,
'barStartsAtCenter': 1,
'doAutoReturn': 1,
'autoReturnToCenter': 1,
};
push2.setTouchStripConfiguration(newConfig).then((conf)=>{
console.log("Touch strip configuration successfully set to:",conf);
}).catch(console.error);
Or you can specify just a subset of properties to change. Others remain unchanged.
push2.setTouchStripConfiguration({'LEDsControlledByPushOrHost':1}).then((conf)=>{
console.log("Touch strip configuration successfully set to:",conf);
}).catch(console.error);
// Output:
// Touch strip configuration successfully set to: TouchStripConfiguration {
// LEDsControlledByHost: 1,
// hostSendsSysex: 0,
// valuesSentAsModWheel: 0,
// LEDsShowPoint: 1,
// barStartsAtCenter: 1,
// doAutoReturn: 1,
// autoReturnToCenter: 1 }
Calling setTouchStripConfiguration
with no arguments will reset to defaults.
push2.setTouchStripConfiguration(); // Resets to defaults
It is not necessary to handle the callback if you don't care to validate the result.
See Touch Strip Configuration Documentation for details on how they work.
Display backlight brightness ranges from 0 to 255. Note that when Push 2 is on USB power the brightness is automatically reduced to 100.
Push2.getDisplayBrightness
returns a Promise.
push2.getDisplayBrightness()
.then((value)=>console.log("Display brightness: "+value))
.catch((err)=>console.log("Error getting display brightness.",err));
Push2.setDisplayBrightness
also returns a Promise which resolves after
verifying that the value was set correctly.
push2.setDisplayBrightness(200)
.then(()=>console.log("Display brightness successfully set."))
.catch((err)=>console.log("Error setting display brightness.",err));
Events from easymidi are accessible from the Push2.midi property.
See midi.md for more on low-level access to MIDI messages.
// Listen to all MIDI messages
push2.midi.on('message', console.log);
// Or listen to specific midi messages (easymidi message type names)
push2.midi.on('noteon', function(msg){
console.log(`"noteon": note: ${msg.note}, velocity:${msg.velocity}, channel:${msg.channel}`);
});
A few handy scripts are included in scripts/
.
First probe MIDI ports to make sure node can see your Push 2 with:
$ cd node-ableton-push2
$ node scripts/show_ports.js
Input ports:
Ableton Push 2 Live Port
Ableton Push 2 User Port
Output ports:
Ableton Push 2 Live Port
Ableton Push 2 User Port
Monitor messages from Push 2.
$ node scripts/monitor_push.js
Ableton Push 2 User Port { channel: 0, note: 36, velocity: 56, _type: 'noteon' }
pad 1,8 pressed, velocity: 56
Ableton Push 2 User Port { channel: 0, note: 36, velocity: 0, _type: 'noteoff' }
pad 1,8 released
Ableton Push 2 User Port { channel: 0, controller: 85, value: 127, _type: 'cc' }
play: 127
Ableton Push 2 User Port { channel: 0, controller: 85, value: 0, _type: 'cc' }
play: 0
...
^C
(Exit with ctrl+C)
Summary of MIDI Messages
Ableton Push 2 MIDI And Display Interface Manual
Generated using TypeDoc


piconv -- iconv(1), reinvented in perl
piconv [-f from_encoding] [-t to_encoding]
[-p|--perlqq|--htmlcref|--xmlcref] [-C N|-c] [-D] [-S scheme]
[-s string|file...]
piconv -l
piconv -r encoding_alias
piconv -h
piconv is perl version of iconv, a character encoding converter
widely available for various Unixen today. This script was primarily a
technology demonstrator for Perl 5.8.0, but you can use piconv in the place of
iconv for virtually any case.
piconv converts the character encoding of either STDIN or files
specified in the argument and prints out to STDOUT.
Here is the list of options. Some options can be in short format
(-f) or long (--from) one.
- -f,--from from_encoding
- Specifies the encoding you are converting from. Unlike iconv, this
option can be omitted. In such cases, the current locale is used.
- -t,--to to_encoding
- Specifies the encoding you are converting to. Unlike iconv, this
option can be omitted. In such cases, the current locale is used.
Therefore, when both -f and -t are omitted, piconv just
acts like cat.
- -s,--string string
- uses string instead of file for the source of text.
- -l,--list
- Lists all available encodings, one per line, in case-insensitive order.
Note that only the canonical names are listed; many aliases exist. For
example, the names are case-insensitive, and many standard and common
aliases work, such as "latin1" for "ISO-8859-1", or
"ibm850" instead of "cp850", or "winlatin1"
for "cp1252". See Encode::Supported for a full discussion.
- -r,--resolve encoding_alias
- Resolve encoding_alias to Encode canonical encoding name.
- -C,--check N
- Check the validity of the stream if N = 1. When N = -1,
something interesting happens when it encounters an invalid
character.
- -c
- Same as "-C 1".
- -p,--perlqq
- Transliterate characters missing in encoding to \x{HHHH} where HHHH is the
hexadecimal Unicode code point.
- --htmlcref
- Transliterate characters missing in encoding to &#NNN; where NNN is
the decimal Unicode code point.
- --xmlcref
- Transliterate characters missing in encoding to &#xHHHH; where HHHH is
the hexadecimal Unicode code point.
- -h,--help
- Show usage.
- -D,--debug
- Invokes debugging mode. Primarily for Encode hackers.
- -S,--scheme scheme
- Selects which scheme is to be used for conversion. Available schemes are
as follows:
- from_to
- Uses Encode::from_to for conversion. This is the default.
- decode_encode
- Input strings are decode()d then encode()d. A straight
two-step implementation.
- perlio
- The new perlIO layer is used. NI-S' favorite.
You should use this option if you are using UTF-16 and others
which linefeed is not $/.
Like the -D option, this is also for Encode hackers.
iconv(1) locale(3) Encode Encode::Supported Encode::Alias PerlIO


WITNESS LIST Finance Committee (Subcommittee on Tax System) February 26, 2002 - 1:30P Accountants ON: Lipstet, Ira Attorney/CPA (Texas Society of CPAs), Austin, TX Airlines ON: Foxhall, Nene VP State/Civic Affairs (Continental Airlines Inc.), Houston, TX Hagan, Dan Airline Executive (American Airlines), Fort Worth, TX Ricks, Ron Lawyer (Southwest Arilines), Dallas, TX Chemical ON: Cardwell, Greg Propety Tax (E.I. Dupont), Houston, TX Dipprey, Ron Gov't. Affairs Director (Dow Chemical Co.), Austin, TX Woodrick, James Trade Association (Texas Chemical Council), Austin, TX Health Care Industry ON: Carter, M.D., Patrick Family Practice Physician (Texas Medical Association), Houston, TX Hogue, M.D., Robert Physician (TMA, TAFP), Brownwood, TX Somerville, M.D., Judson Physician (Border Physicians for TMA), Laredo, TX Insurance ON: Davis, Will Attorney (Texas Association of Life & Health Insurance), Austin, TX Hurd, John R. Independent Producer (Texas Independent Producers & Royalty Owners Association), San Antonio, TX Roan, Forrest Attorney (American Insurance Association), Austin, TX Legal Profession ON: Ohlenforst, Cindy Lawyer (Tax Attorney), Dallas, TX Oil & Gas ON: Segner III, Edmund President/Chief of Staff-EOG Resources (EOG Resources, Inc), Houston, TX Public Testimony ON: Barrera, Patricia A. Webb Co. Tax Assessor-Collector (Webb County), Laredo, TX Hankins, Barbara Retired-Volunteer (League of Women Voters of Texas), Austin, TX Kawalski, Tom President (Texas Healthcare & Bioscience Institute), Austin, TX Lavine, Dick Fiscal Analyst (Center for Public Policy Priorities), Austin, TX Reed, Cyrus Project Director (Texas Center for Policy Studies), Austin, TX Senate Finance Subcommittee on Tax ON: Le Bas, James Chief Revenue Estimator (Comptroller of Public Accounts), Austin, TX Technology ON: Motalo, Pat Executive/Software Company (AEA/High Tech Industry), Austin, TX Wurzel, Geoff Gov't. Affairs Manager (National Semiconductor/AEA), Austin, TX Telecommunications ON: Gonzales, Cindy Tax Counsel (Verizon), Irving, TX McKenzie, Shawn SBC-Southwestern Bell (SBC-Southwestern Bell), Austin, TX Stevens, Paul Managing Director-State & Local Taxes (SBC Communications), San Antonio, TX Texas Strategic Economic Developmen ON: Stephens, F. L. (Steve) Retired (Texas Strategic Economic Planning Commission) Texas Taxpayer's & Research Associa ON: Allaway, Bill Economist (Texas Taxpayers & Research Association), Austin, TX Craymer, Dale Economist/Lobbyist (Texas Taxpayers & Research Association), Austin, TX Utilities ON: Craven, Eric V-P/ Gov't. Relations, TX Electric (Texas Electric Cooperatives), Austin, TX Moseley, Marc Vice President Regulatory Affairs (AECT Tax Committee - Utilities), Dallas, TX Smith, Jr., Charles A. Managing Director, Taxes (Reliant Energy), Houston, TX


Applies the changes introduced by one or more commits.
git cherry-pick <commit>
to pick changes from a single commit.git cherry-pick <commit-1> <commit-2>...
to pick changes from all space-separated commits.git cherry-pick <first-commit>..<last-commit>
to pick changes from a range of commits.git cherry-pick (<commit>... | <first-commit>..<last-commit>)
git cherry-pick 3050fc0de # Picks changes from the commit `3050fc0de`
git cherry-pick 3050fc0de c191f90c7
# Picks changes from the commits `3050fc0de`, `c191f90c7` and `0b552a6d4`
git cherry-pick 3050fc0de..c191f90c7
# Picks changes from the commits in the range `3050fc0de` - `c191f90c7`


{% extends 'main/base.html' %}
{% block mainBlock %}
How to use this?!
- Make sure the background you are uploading photos on, is plain. Textured and multicoloured backgrounds can lead to erraneous results.
- Keep an A4 sized paper on the leftmost side (for comparison), and click the photo of the luggage from the top (90 degrees).
- Now click the photo of the object from the side, again keeping an A4 size paper with it (leftmost side of the photo) for comparison.
- Upload the two photos, and click on the Submit button.
{% endblock %}


This site is a gallery for R Markdown templates/themes. Currently, 131 templates/themes are collected, including 22 packages classified in 12 groups. You could view the R Markdown templates by groups, see the live demos, and share your new discoveries. This is still far from a complete collection of R Markdown, thus your recommendations would be appreciated. Feel free to send me an email or contribute to the repo of this site (see the section How to contribute).
R Markdown is a fantastic toolbox in the R community. With R Markdown, you can produce a variety of reproducible documents for communication, including reports, scientific manuscripts, journal articles, theses, books, posters, slides, websites, etc., even if you do not know how to use R itself. They are platform-independent. This ecosystem is still growing fast. R Markdown is a demonstration that shows how much amazing work R can do besides data processing and visualization.
The R Markdown world is huge. After years of using R Markdown, I collected many nice packages, templates, and themes. I found it, however, painful to search a suitable template/theme before I started writing something. Sometimes it was like searching the ocean for a needle. I knew it was somewhere, but I had no idea where it was.
The other day @Cloud2016 shared another nice beamer template. I raised the question again, “how do you guys organize your collection of templates?” Later an idea came into my mind: why not build a website?
That is basically why you see this site.
Note that the R blogdown package is not displayed on this website, as the Hugo theme gallery is already available.
There are five buttons at the sidebar.
On the template page, you can find the thumbnail, the package name, the author, the link to the project webpage, and the link to the demonstration (if any).
install.packages("package_name")
if it is on CRAN. Otherwise, check the project webpage for help.tinytex::install_tinytex()
for installing a smart distribution of LaTeX..Rmd
file, you should see some boilerplate text that includes code chunks. Use the “Knit” button in the RStudio IDE to render the file and preview the output with a single click or use the keyboard shortcut Cmd/Ctrl + Shift + K..Rmd
by:
Ctrl + Alt + I
; OS X: Cmd + Option + I
),For more help getting started in R Markdown, please see the R Markdown website, or read the section Learning materials.
If you would like to share an R Markdown template/theme, simply follow this procedure:


| 1 | /* | |
| 2 | * Licensed to the Apache Software Foundation (ASF) under one | |
| 3 | * or more contributor license agreements. See the NOTICE file | |
| 4 | * distributed with this work for additional information | |
| 5 | * regarding copyright ownership. The ASF licenses this file | |
| 6 | * to you under the Apache License, Version 2.0 (the | |
| 7 | * "License"); you may not use this file except in compliance | |
| 8 | * with the License. You may obtain a copy of the License at | |
| 9 | * | |
| 10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
| 11 | * | |
| 12 | * Unless required by applicable law or agreed to in writing, | |
| 13 | * software distributed under the License is distributed on an | |
| 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| 15 | * KIND, either express or implied. See the License for the | |
| 16 | * specific language governing permissions and limitations | |
| 17 | * under the License. | |
| 18 | */ | |
| 19 | package org.apache.empire.db; | |
| 20 | ||
| 21 | /** | |
| 22 | * This enum allocates the available compare types. | |
| 23 | * | |
| 24 | * | |
| 25 | */ | |
| 26 | public enum DBCmpType | |
| 27 | { | |
| 28 | NONE, // = -1, | |
| 29 | EQUAL, // = 0, | |
| 30 | NOTEQUAL, // = 1, | |
| 31 | LESSTHAN, // = 2, | |
| 32 | MOREOREQUAL, // = 3, | |
| 33 | GREATERTHAN, // = 4, | |
| 34 | LESSOREQUAL, // = 5, | |
| 35 | LIKE, // = 6, | |
| 36 | NOTLIKE, // = 7, | |
| 37 | NULL, // = 8, | |
| 38 | NOTNULL, // = 9, | |
| 39 | BETWEEN, // = 10, | |
| 40 | NOTBETWEEN, // = 11, | |
| 41 | IN, // = 12, | |
| 42 | NOTIN; // = 13, | |
| 43 | | |
| 44 | public static DBCmpType getNullType(DBCmpType type) | |
| 45 | { | |
| 46 | switch(type) | |
| 47 | { | |
| 48 | case NOTEQUAL: | |
| 49 | case MOREOREQUAL: | |
| 50 | case LESSOREQUAL: | |
| 51 | case NOTLIKE: | |
| 52 | case NOTNULL: | |
| 53 | case NOTBETWEEN: | |
| 54 | case NOTIN: | |
| 55 | // Compare with NOT null | |
| 56 |
1
1. getNullType : replaced return value with null for org/apache/empire/db/DBCmpType::getNullType → NO_COVERAGE |
return DBCmpType.NOTNULL; |
| 57 | // Compare with null | |
| 58 |
1
1. getNullType : replaced return value with null for org/apache/empire/db/DBCmpType::getNullType → NO_COVERAGE |
default: return DBCmpType.NULL; |
| 59 | } | |
| 60 | } | |
| 61 | } | |
Mutations | ||
| 56 |
1.1 |
|
| 58 |
1.1 |


Coverage Summary for Class: Warehouse (it.polimi.ingsw.model)
| Class | Class, % | Method, % | Line, % |
|---|---|---|---|
| Warehouse | 100% (1/1) | 86,7% (26/30) | 84,3% (113/134) |
1 package it.polimi.ingsw.model; 2 3 import it.polimi.ingsw.client.reducedmodel.ReducedDepot; 4 import it.polimi.ingsw.client.reducedmodel.ReducedStrongbox; 5 import it.polimi.ingsw.client.reducedmodel.ReducedWarehouse; 6 import it.polimi.ingsw.commons.ResourceType; 7 import it.polimi.ingsw.exceptions.DepotNotFoundException; 8 import it.polimi.ingsw.exceptions.DepotOutOfBoundsException; 9 import it.polimi.ingsw.exceptions.IncompatibleResourceTypeException; 10 import it.polimi.ingsw.exceptions.StrongboxOutOfBoundException; 11 import it.polimi.ingsw.utils.Pair; 12 13 import java.util.*; 14 import java.util.stream.Collectors; 15 16 /** 17 * Class representing the actual warehouse in player board as an object made by a strongbox, three depots and 18 * two possible extra depots (depending on player's active leader effects). 19 */ 20 public class Warehouse implements Cloneable { 21 /** 22 * Map with all the resources available in warehouse, summing of those present into depots, strongbox and extra depots. 23 */ 24 private Map<ResourceType,Integer> availableResources; 25 /** 26 * Array with three instances of normal depot 27 */ 28 private final NormalDepot[] normalDepots; 29 /** 30 * Array of two extra depots that are set to null until an extra depot effect becomes active 31 * @see it.polimi.ingsw.commons.LeaderEffect 32 */ 33 private final ExtraDepot[] extraDepots; 34 /** 35 * Instance of a strongbox 36 */ 37 private final Strongbox strongbox; 38 39 public Warehouse(Map<ResourceType, Integer> availableResources, NormalDepot[] normalDepot, ExtraDepot[] extraDepot, Strongbox strongbox) { 40 this.availableResources = availableResources; 41 this.normalDepots = normalDepot; 42 this.extraDepots = extraDepot; 43 this.strongbox = strongbox; 44 } 45 46 public Warehouse() 47 { 48 this.availableResources = new EnumMap<>(ResourceType.class); 49 this.availableResources.put(ResourceType.servant,0); 50 this.availableResources.put(ResourceType.coin,0); 51 this.availableResources.put(ResourceType.shield,0); 52 this.availableResources.put(ResourceType.stone,0); 53 this.normalDepots = new NormalDepot[3]; 54 this.normalDepots[0] = new NormalDepot(1); 55 this.normalDepots[1] = new NormalDepot(2); 56 this.normalDepots[2] = new NormalDepot(3); 57 this.extraDepots = new ExtraDepot[2]; 58 this.strongbox = new Strongbox(); 59 } 60 61 62 /** 63 * Initializes an extra depot 64 * @param resourceType the immutable resource that can be stored in the extra depot 65 * @see ExtraDepot 66 */ 67 public void initializeExtraDepot(ResourceType resourceType) 68 { 69 if(this.extraDepots[0] == null) 70 this.extraDepots[0] = new ExtraDepot(resourceType); 71 else 72 if(this.extraDepots[1] == null) 73 this.extraDepots[1] = new ExtraDepot(resourceType); 74 } 75 76 77 /** 78 * Stores resources into the strongbox and updates available resources. 79 * @param resources map of resources with non-negative occurrences that have to be stored. 80 */ 81 public void addResourcesToStrongbox(Map<ResourceType,Integer> resources) 82 { 83 strongbox.addResources(resources); 84 this.updateAvailableResources(); 85 } 86 87 /** 88 * Stores a non-negative amount of resource in a normal depot and updates available resources. 89 * @param depotIndex index of the required normal depot. 90 * @param type kind of resource that has to be stored. 91 * @param num occurrences to be stored. 92 * @throws DepotOutOfBoundsException if this addition is not compliant with normal depot size 93 * @throws IncompatibleResourceTypeException if we're attempting to add a type of resource different from the one that is already stored. 94 */ 95 96 public void addResourcesToDepot(int depotIndex, ResourceType type, int num) throws DepotOutOfBoundsException, IncompatibleResourceTypeException { 97 if(!isValidEditing(type,depotIndex)) 98 throw new IncompatibleResourceTypeException(); 99 /* -1 because indexes starts from 1*/ 100 NormalDepot d = this.normalDepots[depotIndex - 1]; 101 if(d.getOcc() == 0) { 102 d.setType(type); 103 d.add(num); 104 } else { 105 if(d.getType()==type) { 106 d.add(num); 107 } else { 108 throw new IncompatibleResourceTypeException(); 109 } 110 } 111 updateAvailableResources(); 112 } 113 114 115 /** 116 * Stores a positive number of resources in an extra depot, found on the basis of which type of resource the player wants to store in it and 117 * updates available resources. 118 * @param type type of resource to be stored. 119 * @param num positive number of occurrences to be stored. 120 * @throws DepotOutOfBoundsException if number of occurrences exceed the size of extra depot or it becomes negative. 121 * @throws DepotNotFoundException if there is no extra depot available to store required type of resource. 122 */ 123 public void addResourcesToExtraDepot(ResourceType type, int num) throws DepotOutOfBoundsException, DepotNotFoundException { 124 List<ExtraDepot> extraDepot = Arrays.stream(extraDepots).filter(x -> x!=null&&x.getType()==type).collect(Collectors.toList()); 125 if(extraDepot.size() == 0) throw new DepotNotFoundException(); 126 else extraDepot.get(0).add(num); 127 this.updateAvailableResources(); 128 } 129 130 131 /** 132 * Removes a non-negative number of occurrences from a normal depot found on the basis of which type of resource the player requires, at the end updates available resources. 133 * @param type type of resource to be removed. 134 * @param occ number of resource to be removed. 135 * @throws DepotNotFoundException if there's no normal depot associated to the kind of resource required. 136 * @throws DepotOutOfBoundsException if number of occurrences becomes negative. 137 */ 138 public void takeResourceFromNormalDepot(ResourceType type, int occ) throws DepotNotFoundException, DepotOutOfBoundsException { 139 int i = findNormalDepotByResourceType(type); 140 normalDepots[i].take(occ); 141 updateAvailableResources(); 142 } 143 144 /** 145 * Removes a map of non-negative occurrences from the strongbox and updates available resources. 146 * @param occurrences map containing resources to be removed. 147 * @throws StrongboxOutOfBoundException if at least of one number of occurrence of resource in the strongbox becomes negative. 148 */ 149 public void takeResourcesFromStrongbox(Map<ResourceType, Integer> occurrences) throws StrongboxOutOfBoundException { 150 strongbox.takeResources(occurrences); 151 updateAvailableResources(); 152 } 153 154 /** 155 * Removes a non-negative number of occurrences from an extra depot found on the basis of resource type chosen, at the end updates available resources. 156 * @param type type of resource to be removed. 157 * @param occ number of resource to be removed. 158 * @throws DepotNotFoundException if there is no extra depot that contains required type of resource. 159 * @throws DepotOutOfBoundsException if number of occurrences in extra depot becomes negative. 160 */ 161 public void takeResourceFromExtraDepot(ResourceType type, int occ) throws DepotNotFoundException, DepotOutOfBoundsException { 162 int i = findExtraDepotByResourceType(type); 163 extraDepots[i].take(occ); 164 updateAvailableResources(); 165 } 166 167 168 /** 169 * Boolean method that checks if the player has enough resources to perform a certain action by comparing map of needed resources with available ones. 170 * @param neededResources resources needed to do an action. 171 * @return the result of the comparison. 172 */ 173 public boolean checkResources(Map<ResourceType,Integer> neededResources) 174 { 175 return neededResources.keySet().stream().allMatch((key) -> (neededResources.get(key) <= this.availableResources.get(key))); 176 } 177 178 /** 179 * Boolean method that checks if the warehouse has a normal depot with a non-negative number of a certain resource type. 180 * @param type resource type required. 181 * @param occ number of resource type required. 182 * @return the result of the check. 183 */ 184 public boolean checkResourceFromNormalDepot(ResourceType type, int occ) { 185 try { 186 int i = findNormalDepotByResourceType(type); 187 if(occ <= normalDepots[i].getOcc()) return true; 188 else return false; 189 } catch (DepotNotFoundException e) { return false; } 190 } 191 192 /** 193 * Boolean method that checks if strongbox has enough occurrences of a certain resource type. 194 * @param type resource type required. 195 * @param occ number of occurrences. 196 * @return the result of the check. 197 */ 198 public boolean checkResourceFromStrongBox(ResourceType type, int occ) { 199 if(strongbox.getResources().get(type) < occ) return false; 200 else return true; 201 } 202 203 /** 204 * Checks whether an extra depot has enough occurrences of a certain resource type. 205 * @param type resource type required. 206 * @param occ number of occurrences. 207 * @return the result of the check. 208 */ 209 public boolean checkResourceFromExtraDepot(ResourceType type, int occ) { 210 try { 211 int i = findExtraDepotByResourceType(type); 212 if(occ <= extraDepots[i].getOcc()) return true; 213 else return false; 214 } catch(DepotNotFoundException e) { return false; } 215 } 216 217 218 /** 219 * Boolean method called by turn controller over player warehouse to check whether a move action from normal depot 220 * to another normal depot is possible or not on the basis of game rules. 221 * @param depotFrom index of starting normal depot. 222 * @param depotTo index of destination normal depot. 223 * @return the result of the check. 224 */ 225 public boolean checkMoveFromNormalDepotToNormalDepot(int depotFrom, int depotTo) { 226 //checks if starting depot is not empty 227 if(normalDepots[depotFrom - 1].getType() == null) { 228 return false; 229 } 230 else { 231 if(normalDepots[depotTo - 1].getType() == null) { 232 //checks if destination depot has less occurrences than starting one 233 if(normalDepots[depotTo - 1].getSize() < normalDepots[depotFrom - 1].getOcc()) return false; 234 else return true; 235 } 236 else 237 { 238 //checks if it is possible to switch resources between normal depots 239 if(normalDepots[depotTo - 1].getOcc() > normalDepots[depotFrom - 1].getSize() || 240 normalDepots[depotFrom - 1].getOcc() > normalDepots[depotTo - 1].getSize()) return false; 241 else return true; 242 } 243 } 244 } 245 246 /** 247 * Boolean method called by turn controller to check if a move action can be performed from a normal depot to an extra depot 248 * comparing number of occurrences and type of resources to be moved. 249 * @param depotFrom index of starting normal depot. 250 * @param occ number of occurrences that have to be moved. 251 * @param extraDepotTo index of extra depot destination. 252 * @return the result of the check. 253 */ 254 public boolean checkMoveFromNormalDepotToExtraDepot(int depotFrom,int occ, int extraDepotTo) { 255 if(extraDepots[extraDepotTo-1] != null) 256 if(normalDepots[depotFrom-1].getType() == extraDepots[extraDepotTo-1].getType() 257 && occ+extraDepots[extraDepotTo-1].getOcc()<=2 258 && normalDepots[depotFrom-1].getOcc()>=occ) 259 return true; 260 return false; 261 } 262 263 /** 264 * Boolean method called by turn controller to check if a move action can be performed from an extra depot to a normal one 265 * comparing number of occurrences and ensuring that the action is compliant with game rules. 266 * @param extraDepotFrom index of starting extra depot. 267 * @param occ number of occurrences that have to be moved. 268 * @param depotTo index of normal depot destination. 269 * @return the result of the check. 270 */ 271 public boolean checkMoveFromExtraDepotToNormalDepot(int extraDepotFrom, int occ, int depotTo){ 272 if(extraDepots[extraDepotFrom-1] != null && (isValidEditing(extraDepots[extraDepotFrom - 1].getType(),depotTo))) { 273 if ((normalDepots[depotTo - 1].getType() == extraDepots[extraDepotFrom - 1].getType() 274 || normalDepots[depotTo - 1].getType() == null) 275 && normalDepots[depotTo - 1].getOcc() + occ <= normalDepots[depotTo - 1].getSize() 276 && extraDepots[extraDepotFrom - 1].getOcc() >= occ) 277 return true; 278 } 279 return false; 280 } 281 282 /** 283 * Moves resources from a normal depot to another normal depot. 284 * It switches the number of occurrences and the resource type creating two new instances of normal depots. 285 * Each time this method is called it creates a new instances for depots involved into the move. 286 * @param depotFrom index of starting normal depot 287 * @param depotTo index of destination normal depot. 288 * @see it.polimi.ingsw.utils.MoveFromNormalToNormalAction 289 */ 290 public void moveFromNormalDepotToNormalDepot(int depotFrom, int depotTo) { 291 ResourceType resourceTemp = normalDepots[depotTo - 1].getType(); 292 int occTemp = normalDepots[depotTo - 1].getOcc(); 293 int sizeTemp = normalDepots[depotTo - 1].getSize(); 294 int sizeTempFrom = normalDepots[depotFrom - 1].getSize(); 295 normalDepots[depotTo - 1] = new NormalDepot(normalDepots[depotFrom - 1].getOcc(),normalDepots[depotFrom - 1].getType(),sizeTemp); 296 normalDepots[depotFrom - 1] = new NormalDepot(occTemp, resourceTemp, sizeTempFrom); 297 } 298 299 /** 300 * Moves resources from a normal depot to an extra one. Each time this method is called it creates a new instances for 301 * depots involved into the move. 302 * @param depotFrom index of normal depot from which the resources are moved. 303 * @param occ number of occurrences to be moved. 304 * @param extraDepotTo index of extra depot destination for the resources. 305 * @see it.polimi.ingsw.utils.MoveFromNormalToExtraAction 306 */ 307 public void moveFromNormalDepotToExtraDepot(int depotFrom, int occ, int extraDepotTo) { 308 NormalDepot normalDepot = normalDepots[depotFrom-1]; 309 normalDepots[depotFrom-1] = new NormalDepot(normalDepot.getOcc()-occ, normalDepot.getType(), normalDepot.getSize()); 310 if(normalDepots[depotFrom-1].getOcc() == 0) normalDepots[depotFrom-1].setType(null); 311 ExtraDepot extraDepot = extraDepots[extraDepotTo-1]; 312 extraDepots[extraDepotTo-1] = new ExtraDepot(extraDepot.getOcc()+occ, extraDepot.getType()); 313 } 314 315 /** 316 * Moves resources from an extra depot to a normal one. Each time this method is called it creates a new instances for 317 * depots involved into the move. 318 * @param extraDepotFrom index of extra depot from which the resources are moved. 319 * @param occ number of occurrences to be moved. 320 * @param depotTo index of normal depot destination for the resources. 321 * @see it.polimi.ingsw.utils.MoveFromExtraToNormalAction 322 */ 323 public void moveFromExtraDepotToNormalDepot(int extraDepotFrom, int occ, int depotTo) { 324 ExtraDepot extraDepot = extraDepots[extraDepotFrom-1]; 325 extraDepots[extraDepotFrom-1] = new ExtraDepot(extraDepot.getOcc()-occ, extraDepot.getType()); 326 NormalDepot normalDepot = normalDepots[depotTo-1]; 327 if(normalDepot.getType() == null){ 328 normalDepots[depotTo-1] = new NormalDepot(normalDepot.getOcc()+occ, extraDepot.getType(), normalDepot.getSize()); 329 } else{ 330 normalDepots[depotTo-1] = new NormalDepot(normalDepot.getOcc()+occ, normalDepot.getType(), normalDepot.getSize()); 331 } 332 } 333 334 /** 335 * Method that searches for a normal depot given a resource type. 336 * @param type resource type that has to be linked to extra depot to find. 337 * @return index of the normal depot (if present). 338 * @throws DepotNotFoundException if the player doesn't have any normal depot with the required resource type 339 */ 340 public int findNormalDepotByResourceType(ResourceType type) throws DepotNotFoundException { 341 for(int i = 0; i < normalDepots.length; i++) { 342 if (normalDepots[i].getType() == type) return i; 343 } 344 throw new DepotNotFoundException(); 345 } 346 347 /** 348 * Method that searches for an extra depot given a resource type. 349 * @param type resource type that has to be linked to extra depot to find. 350 * @return index of extra depot (if present). 351 * @throws DepotNotFoundException if the player doesn't have any extra depot with the required resource type 352 */ 353 public int findExtraDepotByResourceType(ResourceType type) throws DepotNotFoundException { 354 for(int i = 0; i < extraDepots.length; i++) { 355 if(extraDepots[i]!=null) { 356 if(extraDepots[i].getType() == type) return i; 357 } 358 } 359 throw new DepotNotFoundException(); 360 } 361 362 /** 363 * This method calculates number of points gained summing all the resources in warehouse and dividing the result by five. 364 * @return the number of points. 365 */ 366 public int calcVictoryPointsWarehouse() 367 { 368 return availableResources.keySet().stream().map(x -> availableResources.get(x)).reduce(0,Integer::sum)/5; 369 } 370 371 /** 372 * This method checks whether an action over normal depots breaks game rules, in particular way it ensures that at most one 373 * normal depot contains a particular type of resource. 374 * @param resourceType type of resource. 375 * @param depotTo index of the depot to be edited. 376 * @return the result of the check. 377 */ 378 private boolean isValidEditing(ResourceType resourceType, int depotTo) 379 { 380 for(NormalDepot depots: normalDepots) 381 if(resourceType == depots.getType() && depots!=normalDepots[depotTo-1]) 382 return false; 383 return true; 384 } 385 386 /** 387 * Updates map representing all the resources available in warehouse. It collects them starting from 388 * strongbox and then for each one sums occurrences coming from depots with the previous value. 389 */ 390 private void updateAvailableResources(){ 391 EnumMap<ResourceType, Integer> local = ((EnumMap<ResourceType, Integer>) strongbox.getResources()).clone(); 392 for(NormalDepot d : normalDepots){ 393 if(d.getType()!=null) 394 local.put(d.getType(), local.get(d.getType())+d.getOcc()); 395 } 396 for(ExtraDepot e : extraDepots){ 397 if(e!=null) 398 local.put(e.getType(), local.get(e.getType())+e.getOcc()); 399 } 400 this.availableResources = local; 401 } 402 403 public List<NormalDepot> getNormalDepots() { 404 List<NormalDepot> normalDepots = new ArrayList<>(); 405 for (NormalDepot normalDepot : this.normalDepots) 406 normalDepots.add(new NormalDepot(normalDepot.getOcc(), normalDepot.getType(), normalDepot.getSize())); 407 return normalDepots; 408 } 409 410 public List<ExtraDepot> getExtraDepots() { 411 List<ExtraDepot> extraDepots = new ArrayList<>(); 412 for(int i=0;i<this.extraDepots.length && this.extraDepots[i]!=null; i++) 413 extraDepots.add(new ExtraDepot(this.extraDepots[i].getOcc(),this.extraDepots[i].getType())); 414 return extraDepots; 415 } 416 417 418 /** 419 * Unsafe getter method used for test purposes only. 420 */ 421 public ExtraDepot[] getterUnsafeExtraDepots() { 422 return extraDepots; 423 } 424 425 public ReducedWarehouse getReducedVersion(){ 426 ReducedDepot[] reducedNormalDepots = Arrays.stream(normalDepots).map(NormalDepot::getReducedVersion).toArray(ReducedDepot[]::new); 427 ReducedDepot[] reducedExtraDepots = new ReducedDepot[2]; 428 429 for(int i = 0; i < extraDepots.length; i++) { 430 if (extraDepots[i] != null) 431 reducedExtraDepots[i] = extraDepots[i].getReducedVersion(); 432 } 433 ReducedStrongbox reducedStrongbox = strongbox.getReducedVersion(); 434 return new ReducedWarehouse(reducedNormalDepots, reducedExtraDepots, reducedStrongbox, availableResources); 435 } 436 437 /** 438 * Unsafe getter method used for test purposes only. 439 */ 440 public Map<ResourceType, Integer> getAvailableResources() { 441 return availableResources; 442 } 443 444 /** 445 * @return for each Resource Type, the number of occurrences present in the strongbox. 446 */ 447 public List<Pair<ResourceType, Integer>> getResourcesInStrongbox() { 448 List<Pair<ResourceType,Integer>> result = new ArrayList<>(4); 449 strongbox.getResources().forEach((key,value) -> result.add(new Pair<>(key, value))); 450 return result; 451 } 452 453 }


Event Title
An intercollege fest to showcase Josephite hospitality and vigour.
This section is where much of the event description would go. The more you write the better it looks
This section is where much of the event description would go. The more you write the better it looks


Microservices with Spring Cloud Kubernetes Reference Architecture
Andriy Kalashnykov
This Reference Architecture demonstrates design, development, and deployment of Spring Boot microservices on Kubernetes. Each section covers architectural recommendations and configuration for each concern when applicable.
High-level key recommendations:
- Consider Best Practices in Cloud Native Applications and The 12 Factor App
- Keep each microservice in a separate Maven or Gradle project
- Prefer using dependencies when inheriting from parent project instead of using relative path
- Use Spring Initializr a web application that can generate a Spring Boot project structure, fill in your project details, pick your options, and download a bundled up project
This architecture demonstrates a complex Cloud Native application that addresses following concerns:
- Externalized configuration using ConfigMaps, Secrets, and PropertySource
- Kubernetes API server access using ServiceAccounts, Roles, and RoleBindings
- Health checks using Application Probes
- readinessProbe
- livenessProbe
- startupProbe
- Reporting application state using Spring Boot Actuators
- Service discovery across namespaces using DiscoveryClient
- Exposing API documentation using Swagger UI
- Building a Docker image using best practices
- Layering JARs using the Spring Boot plugin
- Observing the application using Prometheus exporters
Reference Architecture
The reference architecture demonstrates an organization where each unit has its own application designed using a microservices architecture. Microservices are exposed as REST APIs utilizing Spring Boot in an embedded Tomcat server and deployed on Kubernetes.
Each microservice is deployed in its own namespace. Placing microservices in separate namespaces allows logical grouping that makes it easier to manage access privileges by assigning them only to a team responsible for a particular application. Spring Cloud Kubernetes Discovery Client makes internal communication between microservices seamless. It communicates with the Kubernetes API to discover the IPs of all services, running in Pods.
The application implemented in this Reference Architecture is built with several open source components, commonly found in most Spring Boot microservices deployments. These include:
-
Spring Cloud Kubernetes: Provides integration with the Kubernetes API server to allow service discovery, configuration and load balancing used by Spring Cloud.
-
Spring Cloud: provides tools for developers to quickly build common patterns in distributed systems.
- OpenFeign - Java to HTTP client binder
- Ribbon (via Kubernetes services) - client-side load balancing in calls to another microservice
- Zuul - routing and filtering requests to a microservice
- Sleuth - a distributed tracing tool
- Swagger - a set of open-source tools built around the OpenAPI Specification that can design, build, document and consume REST APIs
- Swagger UI - provides interaction and visualization of API resources without writing custom logic.
Reference Architecture Environment
Each microservice runs in its own container, one container per pod and one pod per service replica. The application is built using a microservices architecture and represented by replicated containers calling each other.
Spring Cloud Kubernetes
Spring Cloud Kubernetes provides Spring Cloud implementations of common interfaces that consume Kubernetes native services. Its objective is to facilitate integration of Spring Cloud and Spring Boot applications running inside Kubernetes.
Spring Cloud Kubernetes integrates with the Kubernetes API and allows service discovery, configuration, and load balancing. This Reference Architecture demonstrates the use of the following Spring Cloud Kubernetes features:
- Discovering services across all namespaces using the Spring Cloud DiscoveryClient
- Using ConfigMap and Secrets as Spring Boot property sources with Spring Cloud Kubernetes Config
- Implementing health checks using Spring Cloud Kubernetes pod health indicator
Get source code
The sample application’s source code is located at
git@github.com:AndriyKalashnykov/spring-microservices-k8s.git
and is available
under the master
branch.
Run the following command to clone the repository:
git clone git@github.com:AndriyKalashnykov/spring-microservices-k8s.git
cd spring-microservices-k8s
git checkout tags/1.0.1
Source Code Directory Structure
The application’s source code is structured as follows:
$ tree -d -L 2
.
├── department-service # department microservice project
│ └── src
├── employee-service # employee microservice project
│ └── src
├── gateway-service # gateway project
│ └── src
├── k8s # kubernetes YAML manifests
├── organization-service # organization microservice project
│ └── src
└── scripts # shell scripts to create, build, install, and deploy microservices
10 directories
Enable Spring Cloud Kubernetes
Add the following dependency to enable Spring Cloud Kubernetes features in the project. The library contains modules for service discovery, configuration, and Ribbon load balancing.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-all</artifactId>
</dependency>
Enable Service Discovery Across All Namespaces
Spring Cloud Kubernetes achieves Kubernetes service discovery with Spring Boot applications by providing an implementation of DiscoveryClient. Ribbon client communicates directly to pods rather than through Kubernetes services. This enables more complete balancing as opposed to round robin, which is forced when going through Kubernetes services. Ribbon is used by a higher-level HTTP client, OpenFeign. MongoDB is used for data storage. Swagger2 is an open source project used to generate the REST API documents for RESTful web services.
/spring-microservices-k8s/department-service/src/main/java/vmware/services/department/DepartmentApplication.java
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableMongoRepositories
@EnableSwagger2
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = RibbonConfiguration.class)
public class DepartmentApplication {
public static void main(String[] args) {
SpringApplication.run(DepartmentApplication.class, args);
}
@Bean
public Docket swaggerApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("vmware.services.department.controller"))
.paths(PathSelectors.any())
.build()
.apiInfo(new ApiInfoBuilder().version("1.0").title("Department API").description("Documentation Department API v1.0").build());
}
}
To enable discovery across all namespaces, the all-namespaces
property needs
to be set to true
/spring-microservices-k8s/k8s/department-configmap.yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: department
data:
spring.cloud.kubernetes.discovery.all-namespaces: "true"
# other config removed for brevity.
Feign is a declarative web service client. In order to communicate with
employee-service
from department-service
we need to create an interface and
use the @FeignClient
annotation. In the @FeignClient
annotation the String
value "employee"
is an arbitrary client name, which is used to create a Ribbon
load balancer. The name of the bean in the application context is the fully
qualified name of the interface. At run-time, employee
will be resolved with a
look up in the discovery server to a IP address of employee
service in Kubernetes.
/spring-microservices-k8s/department-service/src/main/java/vmware/services/department/client/EmployeeClient.java
@FeignClient(name = "employee")
public interface EmployeeClient {
@GetMapping("/department/{departmentId}")
List<Employee> findByDepartment(@PathVariable("departmentId") String departmentId);
}
Create Kubernetes namespaces
Create Kubernetes namespaces for each microservice
kubectl create namespace department
kubectl create namespace employee
kubectl create namespace gateway
kubectl create namespace organization
kubectl create namespace mongo
Configure Spring Cloud Kubernetes to access Kubernetes API
Spring Cloud Kubernetes requires access to the Kubernetes API in order to be
able to retrieve the list of IP addresses of the pods that are fronted by a
single service. The simplest way to do that is to define a ClusterRole
with
list of resources and verbs.
/spring-microservices-k8s/k8s/rbac-cluster-role.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: microservices-kubernetes-namespace-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["configmaps", "pods", "services", "endpoints", "secrets"]
verbs: ["get", "list", "watch"]
Then create it in the default
namespace
kubectl apply -f ../k8s/rbac-cluster-role.yaml -n default
create service accounts api-service-account
kubectl create serviceaccount api-service-account -n department
kubectl create serviceaccount api-service-account -n employee
kubectl create serviceaccount api-service-account -n gateway
kubectl create serviceaccount api-service-account -n organization
kubectl create serviceaccount api-service-account -n mongo
bind Service Accounts api-service-account
from each namespace to
ClusterRole
kubectl create clusterrolebinding service-pod-reader-department --clusterrole=microservices-kubernetes-namespace-reader --serviceaccount=department:api-service-account
kubectl create clusterrolebinding service-pod-reader-employee --clusterrole=microservices-kubernetes-namespace-reader --serviceaccount=employee:api-service-account
kubectl create clusterrolebinding service-pod-reader-gateway --clusterrole=microservices-kubernetes-namespace-reader --serviceaccount=gateway:api-service-account
kubectl create clusterrolebinding service-pod-reader-organization --clusterrole=microservices-kubernetes-namespace-reader --serviceaccount=organization:api-service-account
kubectl create clusterrolebinding service-pod-reader-mongo --clusterrole=microservices-kubernetes-namespace-reader --serviceaccount=mongo:api-service-account
and make sure that deployment manifests for microservices reference service account
api-service-account
-
/spring-microservices-k8s/k8s/department-deployment.yaml
-
/spring-microservices-k8s/k8s/employee-deployment.yaml
-
/spring-microservices-k8s/k8s/organization-deployment.yaml
# other config removed for brevity. serviceAccountName: api-service-account
Kubernetes Service Naming
Every Service defined in the cluster (including the DNS server itself) is
assigned a DNS name. “Normal” (not headless) Services are assigned a DNS A or
AAAA record, depending on the IP family of the service, for a name of the form
my-svc.my-namespace.svc.cluster-domain.example
. This resolves to the cluster
IP of the Service.
The DNS records for each service are as follows.
Configure MongoDB
MongoDB deployment
The MongoDB deployment is instructed to read a ConfigMap to get the database-name
and read the Secret to get database-user
and database-password
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb
labels:
app: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: mongo:4.2.3
ports:
- containerPort: 27017
env:
- name: MONGO_INITDB_DATABASE
valueFrom:
configMapKeyRef:
name: mongodb
key: database-name
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongodb
key: database-user
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongodb
key: database-password
resources:
requests:
cpu: "0.2"
memory: 300Mi
limits:
cpu: "1.0"
memory: 300Mi
readinessProbe:
tcpSocket:
port: 27017
initialDelaySeconds: 50
timeoutSeconds: 2
periodSeconds: 20
failureThreshold: 5
livenessProbe:
tcpSocket:
port: 27017
initialDelaySeconds: 50
timeoutSeconds: 2
periodSeconds: 20
failureThreshold: 5
serviceAccountName: api-service-account
MongoDB ConfigMap
In a ConfigMap the name of the Mongo database is set under database-name
.
apiVersion: v1
kind: ConfigMap
metadata:
name: mongodb
data:
database-name: admin
MongoDB Secret
The username and password is set under database-user
and database-password
respectively. Since this is a Secret object, the values must be base64 encoded.
apiVersion: v1
kind: Secret
metadata:
name: mongodb
type: Opaque
data:
database-user: bW9uZ28tYWRtaW4=
database-password: bW9uZ28tYWRtaW4tcGFzc3dvcmQ=
Use Spring Cloud Kubernetes ConfigMap PropertySource
By using the Spring Cloud Kubernetes PropertySources you’re coupling the application to Kubernetes. Without PropertySources, Kubernetes provides mechanisms like mounting configurations and secrets to the containers file system. This enables an application to ingest configuration without knowledge of where it is running.
It’s advised you consider these trade-offs before proceeding. We use PropertySource to access ConfigMap for demonstration purposes, thinking that it will be used by many Spring developers because of it ease of use and convenience.
The Spring Cloud Kubernetes PropertySource feature enables consumption of
ConfigMap
and Secret
objects directly in the application without injecting them into a
Deployment.
The default behavior is based on metadata.name
inside ConfigMap or Secret,
which has to be the same as an application name defined by spring.application.name
property.
For example, the department-service
breaks down as follows.
-
/spring-microservices-k8s/department-service/src/resources/bootstrap.yml
spring: application: name: department # other config removed for brevity.
-
/spring-microservices-k8s/k8s/department-configmap.yaml
kind: ConfigMap apiVersion: v1 metadata: name: department data: logging.pattern.console: "%clr(%d{yy-MM-dd E HH:mm:ss.SSS}){blue} %clr(%-5p) %clr(${PID}){faint} %clr(---){faint} %clr([%8.15t]){cyan} %clr(%-40.40logger{0}){blue} %clr(:){red} %clr(%m){faint}%n" spring.cloud.kubernetes.discovery.all-namespaces: "true" spring.data.mongodb.database: "admin" spring.data.mongodb.host: "mongodb.mongo.svc.cluster.local" spring.output.ansi.enabled: "ALWAYS"
data.logging.pattern.console
: define log patterndata.spring.cloud.kubernetes.discovery.all-namespaces
: allow multi-namespace discoveryspring.data.mongodb.database
: Mongo DB namespring.data.mongodb.host
: Mongo DB locationspring.output.ansi.enabled
: Enforce ANSI output
Use Secret through mounted volume
Kubernetes has the notion of Secrets for storing sensitive data such as passwords, OAuth tokens, etc.
While there are several ways to share secrets with a container, we recommend sharing secrets through mounted volumes. If you enable consuming secrets through the API, we recommend limiting access with RBAC authorization policies.
NOTE: secrets are not secure, they are merely an obfuscation
In this example, a secret named department
is mounted to the file
/etc/secretspot
via volume mongodb
for the following microservices:
department-service
, employee-service
, organization-service
.
/spring-microservices-k8s/department-service/src/main/resources/bootstrap.yaml
spring:
application:
name: department
cloud:
kubernetes:
secrets:
enabled: true
paths:
- /etc/secretspot
enableApi: false
# other config removed for brevity.
/spring-microservices-k8s/k8s/department-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: department
labels:
app: department
spec:
replicas: 1
selector:
matchLabels:
app: department
template:
metadata:
labels:
app: department
spec:
containers:
volumeMounts:
- name: mongodb
mountPath: /etc/secretspot
volumes:
- name: mongodb
secret:
secretName: department
# other config removed for brevity.
Mongo credentials are defined inside Secret object /spring-microservices-k8s/k8s/department-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: department
type: Opaque
data:
spring.data.mongodb.username: bW9uZ28tYWRtaW4=
spring.data.mongodb.password: bW9uZ28tYWRtaW4tcGFzc3dvcmQ=
Use Spring Boot Actuator to export metrics for Prometheus
Prometheus is an open-source monitoring system. Spring
Boot uses Micrometer, an application metrics facade to
integrate Spring Boot
Actuator
metrics with external monitoring systems. It supports several monitoring systems
like Prometheus, Netflix Atlas, AWS CloudWatch, Datadog, InfluxData, SignalFx,
Graphite, Wavefront, etc. The
spring-boot-starter-actuator
module provides all of Spring Boot’s production-ready
features.
To integrate Actuator with Prometheus, you need to add following dependencies in maven project for each microservice:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
configure metrics
and prometheus
endpoints in ConfigMap for each project
(department-configmap.yaml
, employee-configmap.yaml
, organization-configmap.yaml
):
kind: ConfigMap
apiVersion: v1
# removed for brevity
data:
# removed for brevity
management.endpoints.web.exposure.include: "health,info,metrics,prometheus"
management.metrics.enable.all: "true"
and add MeterRegistryCustomizer
for Grafana Spring Boot 2.1
Statistics dashboard in following
@SpringBootApplication
classes: DepartmentApplication
,
EmployeeApplication
, GatewayApplication
, OrganizationApplication
make sure
to set .commonTags("applicsation", "<microservice_name>")
to actual
microservice name:
import io.micrometer.core.instrument.MeterRegistry;
// removed for brevity
public class DepartmentApplication {
// removed for brevity
@Bean
MeterRegistryCustomizer meterRegistryCustomizer(MeterRegistry meterRegistry){
return registry -> {
meterRegistry.config().commonTags("application", "department");
};
}
}
Exposed endpoints are available at http://<host>:<port>/actuator/metrics
and
http://<host>:<port>/actuator/prometheus
with metrics data formatted specifically for Prometheus.
Build Spring Applications
Building department-service
As of Spring Boot 2.3.1.RELEASE, Spring Boot can help you package up Spring Boot applications into Docker images with Layered Jars
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
</plugins>
</build>
After building the project with layered JARs using jarmode
support, install the
package using Maven.
cd /spring-microservices-k8s/department-service/
mvn clean install
The resulting JAR can be tested to check that layers were added.
cd /spring-microservices-k8s/department-service/
java -Djarmode=layertools -jar target/department-service-1.1.jar list
The commands above will produce the following output and create the following directories. they should be added:
- dependencies
- snapshot-dependencies
- spring-boot-loader
- application
Build Docker Images for Spring Applications
To create images compliant with best practices, Dockerfile
s must
consider the following.
- Removing build dependencies from runtime
- via multi-stage builds
- Putting dependencies (JARs) in their own layer
- Layering the application JAR
- Ensuring image pushes only contain changed files
- Running as non-root and using a minimal runtime image
- via use of the distroless image
ARG MVN_VERSION=3.6.3
ARG JDK_VERSION=11
FROM maven:${MVN_VERSION}-jdk-${JDK_VERSION}-slim as build
WORKDIR /build
COPY pom.xml .
# creates layer with maven dependencies
# first build will be significantly slower than subsequent
RUN mvn dependency:go-offline
COPY ./pom.xml /tmp/
COPY ./src /tmp/src/
WORKDIR /tmp/
# build the project
RUN mvn clean package
# extract JAR Layers
WORKDIR /tmp/target
RUN java -Djarmode=layertools -jar *.jar extract
# runtime image
FROM gcr.io/distroless/java:${JDK_VERSION} as runtime
USER nonroot:nonroot
WORKDIR /application
# copy layers from build image to runtime image as nonroot user
COPY --from=build --chown=nonroot:nonroot /tmp/target/dependencies/ ./
COPY --from=build --chown=nonroot:nonroot /tmp/target/snapshot-dependencies/ ./
COPY --from=build --chown=nonroot:nonroot /tmp/target/spring-boot-loader/ ./
COPY --from=build --chown=nonroot:nonroot /tmp/target/application/ ./
EXPOSE 8080
ENV _JAVA_OPTIONS "-XX:MinRAMPercentage=60.0 -XX:MaxRAMPercentage=90.0 \
-Djava.security.egd=file:/dev/./urandom \
-Djava.awt.headless=true -Dfile.encoding=UTF-8 \
-Dspring.output.ansi.enabled=ALWAYS \
-Dspring.profiles.active=default"
# set entrypoint to layered Spring Boot application
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
Build the Docker image for department-service
application
cd /spring-microservices-k8s/department-service/
docker build -t vmware/department:1.1 .
and build the other microservices: employee-service
, gateway-service
,
organization-service
.
List Docker images
docker images
The Dive tool can be used to explore and
analyze Docker images and layers. The below displays the build
image. Docker
Image Id
can be used to explore details of the image.
dive 258a7b40a719
As you can see all 98MB of Maven dependencies have been pushed to a separate layer and can be reused later.
A similar evaluation can be performed for the runtime image. This can be seen below.
dive vmware/department:1.1
All JAR layers created by Spring Boot have been copied to the runtime image.
Deploy a Spring Boot Application
We have created department
namespace, assigned cluster-admin
and built
Docker images in previous steps.
department-service
Kubernetes deployment manifest
apiVersion: apps/v1
kind: Deployment
metadata:
name: department
labels:
app: department
spec:
replicas: 1
selector:
matchLabels:
app: department
template:
metadata:
labels:
app: department
spec:
containers:
- name: department
image: vmware/department:1.1
ports:
- containerPort: 8080
resources:
requests:
cpu: "0.2"
memory: 300Mi
limits:
cpu: "1.0"
memory: 300Mi
readinessProbe:
httpGet:
port: 8080
path: /actuator/health
initialDelaySeconds: 60
timeoutSeconds: 2
periodSeconds: 20
failureThreshold: 5
livenessProbe:
httpGet:
port: 8080
path: /actuator/info
initialDelaySeconds: 60
timeoutSeconds: 2
periodSeconds: 20
failureThreshold: 5
serviceAccountName: api-service-account
Workloads should always explicitly specify
spec.template.spec.containers.resources.requests
and
spec.template.spec.containers.resources.limits
and keep
spec.template.spec.containers.resources.requests.memory
and
spec.template.spec.containers.resources.limits.memory
equal since RAM is not a
compressible resource like CPU.
Application probes recommendations:
readinessProbe
: Always; makes app available to traffic after checks pass.livenessProbe
: Usually; should only check for conditions where restarting the application is an appropriate solution. Be sure to not link this check to external dependencies, such as database connectivity, or it may introduce cascading failures. A good check can be hitting an endpoint, allocating some memory (e.g. byte array) and returning200
.startupProbe
: Rarely; Readiness and liveness probes (with a delay) can typically solve the use case startupProbes attempt to solve for. If a specific check is appropriate for an application that has a slow startup and the check should never be run again, astartupProbe
may be justified.
Kubernetes leverages probes to determine if the app is ready to accept traffic and whether the app is alive
- If
readinessProbe
does not return code 200 - no traffic will be routed to it - If
livenessProbe
does not return code 200 - Kubernetes restarts the Pod
Spring Boot has a built-in set of endpoints from the Actuator module:
- /actuator/health : provides basic application health information
- /actuator/info: provides arbitrary application info
To add the Actuator to a Maven based project, add the following ‘Starter’ dependency.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
To deploy department-service
onto Kubernetes execute following command.
kubectl apply -n department -f ./spring-microservices-k8s/k8s/department-configmap.yaml
Octant - is a great open-source tool that can can graphically visualize Kubernetes objects dependencies, forward local ports to a running pod, inspect pod logs, navigate through different clusters and help developers to understand how applications run on a Kubernetes.
Overview of the department
namespace
Resources associated with the pod deployed in the department
namespace
Expose a Service
We use port 8080 and NodePort type to expose all microservices. The following service definition groups all pods labeled with the field app
equal to department
apiVersion: v1
kind: Service
metadata:
name: department
labels:
app: department
spec:
ports:
- port: 8080
protocol: TCP
selector:
app: department
type: NodePort
Configure Gateway service
We use gateway-service
service to expose auto-generated Swagger documentation
via Swagger UI web interface for all microservices distributed across multiple
namespaces. To do that we use Spring Cloud Netflix Zuul which integrates with
Kubernetes discovery via Ribbon client.
Required dependencies:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
Configuration of the routes is straightforward and uses Spring Cloud Kubernetes Discovery.
apiVersion: v1
kind: ConfigMap
metadata:
name: gateway
data:
logging.pattern.console: "%d{HH:mm:ss} ${LOG_LEVEL_PATTERN:-%5p} %m%n"
spring.cloud.kubernetes.discovery.all-namespaces: "true"
zuul.routes.department.path: "/department/**"
zuul.routes.employee.path: "/employee/**"
zuul.routes.organization.path: "/organization/**"
Since Zuul proxy is automatically integrated with DiscoveryClient it is easy to configure dynamic resolution of microservices endpoints by Swagger.
@Configuration
public class GatewayApi {
@Autowired
ZuulProperties properties;
@Primary
@Bean
public SwaggerResourcesProvider swaggerResourcesProvider() {
return () -> {
List<SwaggerResource> resources = new ArrayList<>();
properties.getRoutes().values().stream()
.forEach(route -> resources.add(createResource(route.getId(), "2.0")));
return resources;
};
}
private SwaggerResource createResource(String location, String version) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(location);
swaggerResource.setLocation("/" + location + "/v2/api-docs");
swaggerResource.setSwaggerVersion(version);
return swaggerResource;
}
}
Configure Ribbon client to allow discovery across all namespaces:
public class RibbonConfiguration {
@Autowired
private DiscoveryClient discoveryClient;
private String serviceId = "client";
protected static final String VALUE_NOT_SET = "__not__set__";
protected static final String DEFAULT_NAMESPACE = "ribbon";
public RibbonConfiguration () {
}
public RibbonConfiguration (String serviceId) {
this.serviceId = serviceId;
}
@Bean
@ConditionalOnMissingBean
public ServerList<?> ribbonServerList(IClientConfig config) {
Server[] servers = discoveryClient.getInstances(config.getClientName()).stream()
.map(i -> new Server(i.getHost(), i.getPort()))
.toArray(Server[]::new);
return new StaticServerList(servers);
}
}
Ribbon configuration needs to be applied in the main class of the application:
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
@EnableSwagger2
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = RibbonConfiguration.class)
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
Gateway Swagger UI
Swagger UI is exposed on the Gateway service and showcases an advantage of cross
namespace discovery where employee-service
, department-service
add
organization-service
are available via gateway-service
Configure Ingress
Kubernetes Ingress is a collection of rules that allow incoming requests to
reach the downstream services. It works almost as a service with type
LoadBalancer, but you can set custom routing rules. YAML manifest needs to set
host
to a hostname under which the gateway will be available. In our
microservices architecture, Ingress has a role of an API gateway.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: gateway-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
backend:
serviceName: default-http-backend
servicePort: 80
rules:
- host: microservices-cluster.info
http:
paths:
- path: /employee
backend:
serviceName: employee
servicePort: 8080
- path: /department
backend:
serviceName: department
servicePort: 8080
- path: /organization
backend:
serviceName: organization
servicePort: 8080
Execute the following commands to apply the configuration above to the Kubernetes cluster
cd /spring-microservices-k8s/k8s/
kubectl apply -f ingress.yaml
Testing Ingress
Execute following commands to populate MongoDB with test data
cd /spring-microservices-k8s/scrips/
./populate-data.sh
Execute following command to check that department-service
is available via Ingress
curl http://microservices-cluster.info/department/1/with-employees | jq
Example output from department-service
{
"address": "Main Street",
"id": "1",
"name": "MegaCorp"
"departments": [
{
"employees": [
{
"age": 25,
"id": 1,
"name": "Smith",
"position": "engineer"
},
{
"age": 45,
"id": 2,
"name": "Johns",
"position": "manager"
}
],
"id": 1,
"name": "RD Dept."
}
],
"employees": [
{
"age": 25,
"id": 1,
"name": "Smith",
"position": "engineer"
},
{
"age": 45,
"id": 2,
"name": "Johns",
"position": "manager"
}
],
}


--- layout: default home: true ---
Indicate each of successively presented letters whether it is a target or not.
Get familiar with the task through practice.