top
is generally a nice utility for that kind of tasks. But, there is a better and cleaner alternative: htop
. A comparison picture is worth a thousand words.Install it on OSX using:
brew install htop
top
is generally a nice utility for that kind of tasks. But, there is a better and cleaner alternative: htop
. A comparison picture is worth a thousand words.brew install htop
meteor create Polaroid cd PolaroidNow create a common fullstack JS directory structure only targeted for a client WebApp without server side integration:
mkdir -p client/lib client/models client/startup client/stylesheets client/views lib packages public/imgRemove the automatically created files:
rm -rf Polaroid.*Add the following package with Meteor:
meteor add coffeescript meteor add stylusAnd this one with Meteorite:
mrt add jade mrt add famonoEasy as pie.
public/img/camera.png
that you'll find in the Zip file that Famo.us provides in their download section.
client/stylesheets/app.styl
is kept as its minimum as most of the CSS rules are handled by Famo.us.
@import 'nib' html background: #404040 body -webkit-touch-callout: none user-select: none font-family: 'AvenirNext-Medium'
client/index.jade
is kept as its minimum as most of the tags are handled by Famo.ushead title Famo.us Polaroid body +index template(name='index')That's it, a simple
template
loaded by Meteor.
client/lib/polaroid.coffee
:
# Declare Polaroid namespace window.Polaroid ?= {}
client/lib/famous.coffee
:
# Declare Famo.us namespace window.Famous ?= {}
lib
directory is used as it is loaded first by Meteor.
client/models/slidedata.coffee
:Polaroid.SlideData = userId: "109813050055185479846" albumId: "6013105701911614529" picasaUrl: "https://picasaweb.google.com/data/feed/api/user/" queryParams: "?alt=json&hl=en_US&access=visible&fields=entry(id,media:group(media:content,media:description,media:keywords,media:title))" defaultImage: "https://lh4.googleusercontent.com/-HbYp2q1BZfQ/U3LXxmWoy7I/AAAAAAAAAJk/VqI5bGooDaA/s1178-no/1.jpg" getUrl: -> @picasaUrl + @userId + "/albumid/" + @albumId + @queryParams parse: (data) -> urls = [] data = JSON.parse(data) entries = data.feed.entry i = 0 while i < entries.length media = entries[i].media$group urls.push media.media$content[0].url i++ urlsA simple dictionary with 2 methods.
client/startup/famous.coffee
to load the polyfills and to create the Famo.us's singleton so that if I enhance this WebApp with multiple page loaded with a router, there will be no additional loadings or instantiations.
# Import famous.css require 'famous/core/famous' # Adds the famo.us dependencies require 'famous-polyfills' # Wait for document ready $(document).ready -> # Load Famo.us libraries Famous.Engine = require 'famous/core/Engine' Famous.View = require 'famous/core/View' Famous.Transform = require 'famous/core/Transform' Famous.Surface = require 'famous/core/Surface' Famous.StateModifier = require 'famous/modifiers/StateModifier' Famous.Timer = require 'famous/utilities/Timer' Famous.ImageSurface = require 'famous/surfaces/ImageSurface' Famous.ContainerSurface = require 'famous/surfaces/ContainerSurface' Famous.Lightbox = require 'famous/views/Lightbox' Famous.Utility = require 'famous/utilities/Utility' Famous.Easing = require 'famous/transitions/Easing' Famous.ContainerSurface = require 'famous/surfaces/ContainerSurface' Famous.Transitionable = require 'famous/transitions/Transitionable' Famous.SpringTransition = require 'famous/transitions/SpringTransition' # Register transitions Famous.Transitionable.registerMethod 'spring', Famous.SpringTransition # Create main context Polaroid.mainCtx = Famous.Engine.createContext()
client/index.coffee
that goes along with our Jade file:Template.index.rendered = -> # Get request to Picasa API Famous.Utility.loadURL Polaroid.SlideData.getUrl(), (data) -> data = Polaroid.SlideData.parse data # Instantiate the AppView with our URL's data Polaroid.appView = new Polaroid.AppView data: data Polaroid.mainCtx.add Polaroid.appView
client/views/appview.coffee
$(document).ready -> class Polaroid.AppView extends Famous.View DEFAULT_OPTIONS: data: undefined cameraWidth: 0.6 * window.innerHeight constructor: (@options) -> @DEFAULT_OPTIONS.slideWidth = 0.8 * @DEFAULT_OPTIONS.cameraWidth @DEFAULT_OPTIONS.slideHeight = @DEFAULT_OPTIONS.slideWidth + 40 @DEFAULT_OPTIONS.slidePosition = 0.77 * @DEFAULT_OPTIONS.cameraWidth @constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS super @options @createCamera() @createSlideshow() createCamera: -> camera = new Famous.ImageSurface size: [@options.cameraWidth, true] content: 'img/camera.png' properties: width: '100%' cameraModifier = new Famous.StateModifier origin: [0.5, 0] align: [0.5, 0] transform: Famous.Transform.behind @add(cameraModifier).add camera createSlideshow: -> slideshowView = new Polaroid.SlideshowView size: [@options.slideWidth, @options.slideHeight] data: @options.data slideshowModifier = new Famous.StateModifier origin: [0.5, 0] align: [0.5, 0] transform: Famous.Transform.translate 0, @options.slidePosition, 0 slideshowContainer = new Famous.ContainerSurface properties: overflow: 'hidden' @add(slideshowModifier).add slideshowContainer slideshowContainer.add slideshowView slideshowContainer.context.setPerspective 1000
client/views/slideshowview.coffee
$(document).ready -> class Polaroid.SlideshowView extends Famous.View DEFAULT_OPTIONS: size: [450, 500] data: undefined lightboxOpts: inOpacity: 1 outOpacity: 0 inOrigin: [0, 0] outOrigin: [0, 0] showOrigin: [0, 0] inTransform: Famous.Transform.thenMove Famous.Transform.rotateX(0.9), [0, -300, -300] outTransform: Famous.Transform.thenMove Famous.Transform.rotateZ(0.7), [0, window.innerHeight, -1000] inTransition: duration: 500, curve: Famous.Easing.outBack outTransition: duration: 350, curve: Famous.Easing.inQuad constructor: (@options) -> @constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS super @options @rootModifier = new Famous.StateModifier size: @options.size origin: [0.5, 0] align: [0.5, 0] @mainNode = @add @rootModifier @createLightbox() @createSlides() createLightbox: -> @lightbox = new Famous.Lightbox @options.lightboxOpts @mainNode.add @lightbox createSlides: => @slides = [] @currentIndex = 0 console.log @options.data for url in @options.data slide = new Polaroid.SlideView size: @options.size photoUrl: url @slides.push slide slide.on 'click', @showNexSlide @showCurrentSlide() showCurrentSlide: -> @ready = false slide = @slides[@currentIndex] @lightbox.show slide, => @ready = true slide.fadeIn() showNexSlide: => return if @ready isnt true @currentIndex++ if @currentIndex is @slides.length @currentIndex = 0 @showCurrentSlide()
client/views/slideview.coffee
$(document).ready -> class Polaroid.SlideView extends Famous.View DEFAULT_OPTIONS: size: [400, 450] filmBorder: 15 photoBorder: 3 photoUrl: Polaroid.SlideData.defaultImage angle: -0.5 constructor: (@options) -> @constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS super @options @rootModifier = new Famous.StateModifier size: @options.size @mainNode = @add @rootModifier @createBackground() @createFilm() @createPhoto() createBackground: -> background = new Famous.Surface properties: backgroundColor: '#fffff5' boxShadow: '0 10px 20px -5px rgba(0, 0, 0, 0.5)' cursor: 'pointer' @mainNode.add background background.on 'click', => @_eventOutput.emit 'click' createFilm: -> @options.filmSize = @options.size[0] - 2 * @options.filmBorder film = new Famous.Surface size: [@options.filmSize, @options.filmSize] properties: backgroundColor: '#222' zIndex: 1 # Make surface invisible to pointer events pointerEvents: 'none' filmModifier = new Famous.StateModifier origin: [0.5, 0] align: [0.5, 0] transform: Famous.Transform.translate 0, @options.filmBorder, 1 @mainNode .add filmModifier .add film createPhoto: -> photoSize = @options.filmSize - 2 * @options.photoBorder photo = new Famous.ImageSurface size: [photoSize, photoSize] content: @options.photoUrl properties: zIndex: 2 # Make surface invisible to pointer events pointerEvents: 'none' @photoModifier = new Famous.StateModifier origin: [0.5, 0] align: [0.5, 0] transform: Famous.Transform.translate 0, @options.filmBorder + @options.photoBorder, 2 opacity: 0.01 @mainNode .add @photoModifier .add photo fadeIn: => @photoModifier.setOpacity 1, {duration: 1500, curve: 'easeIn'} @shake() shake: -> @rootModifier.halt() @rootModifier.setTransform Famous.Transform.rotateX(@options.angle), {duration: 200, curve: 'easeOut'} @rootModifier.setTransform Famous.Transform.identity, {method: 'spring', period: 600, dampingRatio: 0.15}
brew cask install qlcolorcode qlstephen qlmarkdown quicklook-json qlprettypatch quicklook-csv betterzipql webp-quicklook suspicious-package
rm
command, it simplifies it when you are removing a tree of files. Super nice.HeaderFooterLayout
to layout the basis of your SPA:HeaderFooterLayout = require 'famous/views/HeaderFooterLayout' layout = new HeaderFooterLayout headerSize: 100 footerSize: 50 mainContext.add layout layout.header.add new Surface content: 'Header' size: [undefined, 100] classes: ['header-surface'] properties: lineHeight: '100px' textAlign: 'center' layout.content.add new Surface content: 'Content' size: [undefined, undefined] properties: lineHeight: '300px' textAlign: 'center' layout.footer.add new Surface content: 'Footer' size: [undefined, 50] classes: ['footer-surface'] properties: lineHeight: '50px' textAlign: 'center'
pretty
function to prettify your JSON documents or collections within the Mongo's shell:
db.find({}).pretty()Here's the difference with a simple example:
~/.mongorc.js
using this command:
echo "DBQuery.prototype._prettyShell = true" >> ~/.mongorc.js
Thanks to Lee Machin for this tip: https://coderwall.com/p/3vfw9a
npm -g install mongo-hackerNow, here is the same example with some nice hacking:
ssh-copy-id
is a simple tool that send your public SSH key to a remote server. Installing it on OSX:
brew install ssh-copy-id
192.168.1.32
:
ssh-copy-id root@192.168.1.32Note: If you have never generated your SSH key pair, simply issue the following command:
ssh-keygen -t rsa -C
brew install saltstackNow, we are going to increase the opened sockets capacity of OSX:
sudo launchctl limit maxfiles 4096 8192When installed from Homebrew, Saltstack doesn't come with the default configuration files. Theses files are described in the Saltstack documentation pages: 21.6. Configuration file examples. Saltstack expects to see at least 2 files,
master
and minion
into the uncreated /etc/salt
directory. Let's fix that:
sudo mkdir /etc/saltAnd copy the content of
master
and minion
into this freshly created directory.ifconfig -a | perl -nle'/(\d+\.\d+\.\d+\.\d+)/ && print $1'Now edit the /etc/master file to reflects the opened sockets capacity and as I dont' like sudoing each time I have to launch a command, add your username to the allowed users (here it's PEM, of course):
max_open_files: 8192 user: PEM interface: 192.168.1.30 file_roots: base: - /Users/PEM/Projects/SolutionsM3/DevOps/states pillar_roots: base: - /Users/PEM/Projects/SolutionsM3/DevOps/pillarNote that I've setup my Saltstack master so that its all the formulas that I deploy are stored in my personal directory. This allows me to modify every deployed configuration and save them with Git once I've finished working on them. This is what DevOps is for: your infrastructure and administration as simple script files with formulas reproducible, idempotent, evolving, without connecting manually to every servers each time you need to adjust a simple variable.
ssh-keygenThis command generate the following files in your home directory:
/Users/PEM/.ssh ├── id_rsa ├── id_rsa.pub └── known_hostsWe are going to authorize ourselves on our VPS with your public key:
cat ~/.ssh/id_rsa.pub | ssh root@vpsXXXXX.ovh.neta 'cat >> .ssh/authorized_keys'Where
XXXXX
is the VPS's number that OVH has provided you.ssh root@vpsXXXXX.ovh.net
/etc/salt/roster
file with this info:
vpsXXXXX: host: vpsXXXXX.ovh.netNow, whenever I want to check by a ping all my servers, I use a single command:
salt-ssh '*' test.pingIf I want to target a specific one:
salt-ssh 'vpsXXXXX' test.pingWith simple naming scheme, I'm able to achieve deployment of a specific package on a specific group of servers. Nifty.
salt-ssh '*' -r 'apt-get install -y salt-minion'
tree
command on all my servers or checking that it has already been deployed from the comfort of my coach is done like this. In my DevOps project, I've set up 2 files:
├── pillar └── states ├── top.sls └── tree.slsThe
states/top.sls
file declare all the available formulas that I want to apply on every servers. In this simple example it contains only a basic rule to install the tree
command:
base: '*': - treeAnd for the
states/tree.sls
file, just a simple call to the Saltstack's module pkg, which is able to handle almost every Linux packaging tools that I've been playing with:
tree: pkg: - installedTime for the installation. As the Saltstack's state are idempotent, I can run this command every time I want. It will only execute it where it is required:
tree: salt-ssh '*' -c /etc/salt state.highstateWith this installation, I'm capable of checking the tree of files exposed by NGINX on all my servers with a single command:
salt-ssh '*' -r 'tree /var/www'