<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>johnpfeiffer - john pfeiffer</title><link href="https://blog.john-pfeiffer.com/" rel="alternate"/><link href="https://blog.john-pfeiffer.com/feeds/john-pfeiffer.atom.xml" rel="self"/><id>https://blog.john-pfeiffer.com/</id><updated>2026-04-03T23:59:00-07:00</updated><entry><title>AI Natives do not question the gifts from the gods</title><link href="https://blog.john-pfeiffer.com/ai-natives-do-not-question-the-gifts-from-the-gods/" rel="alternate"/><published>2026-04-03T23:59:00-07:00</published><updated>2026-04-03T23:59:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2026-04-03:/ai-natives-do-not-question-the-gifts-from-the-gods/</id><summary type="html">
&lt;h1 id="ai-natives-dont-question-the-gifts-from-the-gods"&gt;AI Natives don't question the gifts from the gods&lt;/h1&gt;
&lt;p&gt;At a recent SF hackathon we used ChatGPT to understand Clinical Trials, defaulted to Claude Code to code up everything, and used LangChain plus Minimax LLMs as the core solution.&lt;/p&gt;
&lt;p&gt;My teammates are AI natives: just graduating from university and they …&lt;/p&gt;</summary><content type="html">
&lt;h1 id="ai-natives-dont-question-the-gifts-from-the-gods"&gt;AI Natives don't question the gifts from the gods&lt;/h1&gt;
&lt;p&gt;At a recent SF hackathon we used ChatGPT to understand Clinical Trials, defaulted to Claude Code to code up everything, and used LangChain plus Minimax LLMs as the core solution.&lt;/p&gt;
&lt;p&gt;My teammates are AI natives: just graduating from university and they don't question the answers at their fingertips.&lt;/p&gt;
&lt;p&gt;Unfazed by the entirely new-to-them problem space of clinicaltrials.gov being a mess. Valuable information with awkward technology and clunky documentation was unlocked in just a few hours of "vibe coding".&lt;/p&gt;
&lt;p&gt;No hesitation to try something, see what happens, and then just prompt again. Any unfamiliar domain term or technical issue is resolved with a quick prompt. No google, no stack overflow.&lt;/p&gt;
&lt;p&gt;It was a joy to work with them and see the new possibilities as they take for granted the powerful capabilities and apply them without hesitation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Prometheus"&gt;https://en.wikipedia.org/wiki/Prometheus&lt;/a&gt; 🔥&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="reflecting-on-arbitrage-or-persistent-advantage"&gt;Reflecting on Arbitrage or Persistent Advantage&lt;/h1&gt;
&lt;p&gt;Extracting and analyzing data with a customized UI/product is now a weekend project, so what makes a business successful?&lt;/p&gt;
&lt;p&gt;"Ship it" hackathon mode means we didn't spend any time thinking about the &lt;a href="https://www.lennysnewsletter.com/p/business-strategy-with-hamilton-helmer"&gt;"7 powers"&lt;/a&gt;. Identifying investment in opportunities based on clinical trials has no network effect, no economies of scale, no entrenched switching costs, no counter-positioning, no cornered resource, no branding, and especially no process power.&lt;/p&gt;
&lt;p&gt;Or as &lt;a href="https://www.berkshirehathaway.com/letters/1986.html"&gt;Warren Buffett popularized&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;...moat that protects a valuable and much-sought-after business castle.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Coding agents raise the software floor dramatically, which increases the speed of competition.&lt;/p&gt;
&lt;p&gt;Cloning or reverse-engineering from an interface/API reduces the "Switching Costs" of migration, as exemplified by the recent fierce AI-powered cloud competition: &lt;a href="https://newsletter.pragmaticengineer.com/p/the-pulse-cloudflare-rewrites-nextjs"&gt;https://newsletter.pragmaticengineer.com/p/the-pulse-cloudflare-rewrites-nextjs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Google DeepMind spun out Isomorphic Labs and Anthropic acquired Coefficient Bio because the major AI players recognize the deep moats in biotech; having very private data is more valuable than ever.&lt;/p&gt;
&lt;h1 id="what-you-get-from-people"&gt;What you get from People&lt;/h1&gt;
&lt;p&gt;When building something is cheaper and faster, how do you differentiate?&lt;/p&gt;
&lt;p&gt;More than a business strategy, you need talented hard-working people who take the initiative because "Culture eats strategy".&lt;/p&gt;
&lt;p&gt;Thanks to the organizers &lt;a href="https://hac.ai/event/detail/52"&gt;Hanwha AI Center&lt;/a&gt; + &lt;a href="https://www.aivalley.io/hackathons/vibe-coding-hacathon-building-ai-for-finance/projects"&gt;AI Valley&lt;/a&gt;, the rooms were abuzz with energy. Everyone was participating in the future: fast experimentation and doing more with less.&lt;/p&gt;
&lt;p&gt;In-person had creativity, spontaneous connections... and loud talking-over-each-other, not enough conference rooms, caffeine shortages, and lines for lunch and the bathroom.&lt;/p&gt;
&lt;h2 id="people-vs-agents-is-a-false-dichotomy"&gt;People vs Agents is a false dichotomy&lt;/h2&gt;
&lt;p&gt;Anthropic and OpenAI have unimaginably high valuations while profitable SaaS incumbents are feeling pressure from the stock market 2026 &lt;a href="https://www.bloomberg.com/news/newsletters/2026-02-04/new-ai-fear-unlocked-as-traders-ditch-companies-at-risk-of-disruption"&gt;"SaaSPocalypse"&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One way of viewing the world is replacing all human employees with AI agents = zero sum.&lt;/p&gt;
&lt;p&gt;A more ambitious vision is augmenting every human in your org to 10x productivity.&lt;/p&gt;
&lt;p&gt;But doubling business productivity doesn't help when your competitor has something magical with an AI-powered product. 🦄&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I believe tackling the hardest problems that AI now unlocks creates huge customer demand... which needs AI-augmented humans to keep up.&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id="too-much-of-a-good-thing"&gt;Too much of a good thing?&lt;/h1&gt;
&lt;p&gt;We were coding so fast that we quickly hit complexity overload and spent the last hour of the hackathon debugging. A &lt;a href="https://web.archive.org/web/20210414115314/http://www.laputan.org/mud/"&gt;ball of mud monolith&lt;/a&gt;, lots of AI-generated code, no tests. =(&lt;/p&gt;
&lt;p&gt;Decades of experience suddenly became invaluable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keeping the "main" core loop tight and easy to read&lt;/li&gt;
&lt;li&gt;Modularized code like separating "external vendor/network calls" into separate clients&lt;/li&gt;
&lt;li&gt;Each specific feature into manageable areas of partitioned complexity&lt;/li&gt;
&lt;li&gt;Compartmentalization of failures to not affect the rest of the application&lt;/li&gt;
&lt;li&gt;Logging, Tests&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We need to question these "gifts from the gods", and know that hard-won principles still matter. Even when we're going as fast as possible for a hackathon demo.&lt;/p&gt;
&lt;p&gt;Of course I'm disappointed we didn't win an award, but I am energized by seeing a glimpse of the future and connecting with the next generation of builders.&lt;/p&gt;</content><category term="leadership"/><category term="ai"/><category term="career"/><category term="engineering"/><category term="management"/><category term="leadership"/><category term="navigating change"/></entry><entry><title>What a Security Hackathon Taught Me About Agents in the Cloud</title><link href="https://blog.john-pfeiffer.com/what-a-security-hackathon-taught-me-about-agents-in-the-cloud/" rel="alternate"/><published>2026-03-16T20:20:00-07:00</published><updated>2026-03-16T20:20:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2026-03-16:/what-a-security-hackathon-taught-me-about-agents-in-the-cloud/</id><summary type="html">
&lt;h1 id="all-you-need-is-agents-and-human-intent"&gt;All you need is Agents and Human intent&lt;/h1&gt;
&lt;p&gt;Took 3rd place (according to the AI judge) and won the "Scariest" award at the NEBULA:FOG, an AI × Security hackathon in San Francisco on March 14, 2026.&lt;/p&gt;
&lt;p&gt;My teammate's genius was giving human intent while 5+ (Claude Opus 4.6) agents …&lt;/p&gt;</summary><content type="html">
&lt;h1 id="all-you-need-is-agents-and-human-intent"&gt;All you need is Agents and Human intent&lt;/h1&gt;
&lt;p&gt;Took 3rd place (according to the AI judge) and won the "Scariest" award at the NEBULA:FOG, an AI × Security hackathon in San Francisco on March 14, 2026.&lt;/p&gt;
&lt;p&gt;My teammate's genius was giving human intent while 5+ (Claude Opus 4.6) agents did the work in the cloud (they'd melt a laptop). They downloaded the code, built and exercised targets, ran exploits, and auto-generated POC findings with evidence. They even created the presentation.&lt;/p&gt;
&lt;p&gt;My role was knowing which tools are widely used in genomics, and observing the future be unevenly distributed. &lt;/p&gt;
&lt;p&gt;&lt;em&gt;Responsible disclosures in progress!&lt;/em&gt;&lt;/p&gt;
&lt;h1 id="security-has-always-mattered"&gt;Security has always mattered&lt;/h1&gt;
&lt;p&gt;The tag line from our presentation was:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Low priority for scientists, High priority for security&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As a teenager reading "The Cuckoo's Egg" by Cliff Stoll, I was inspired by the insights into the earliest days of networked computing and hacking. Systems need to be secure by design, and people have to understand their role for it to work.&lt;/p&gt;
&lt;p&gt;Later, Steven Levy's "Crypto: How the Code Rebels Beat the Government, Saving Privacy in the Digital Age" impressed upon me that a single determined person like Whitfield Diffie could democratize privacy through technology. I also learned how the awesome innovation and implementation of asymmetric cipher keys underpins all secure communication and business today.&lt;/p&gt;
&lt;p&gt;Add Schneier's principles and systematic thinking on security, Krebs's reporting on the actual threat landscape, and Troy Hunt's work (&lt;a href="https://haveibeenpwned.com/"&gt;https://haveibeenpwned.com/&lt;/a&gt;) making breach data accessible to the public, and you understand the worldview that security is not just a feature, it's a transparent and deliberate foundation.&lt;/p&gt;
&lt;h1 id="how-it-happened"&gt;How it happened&lt;/h1&gt;
&lt;p&gt;We focused on key open source projects for genomics. We found a set of vulnerabilities in the tools and dependencies that are hiding in plain sight. They're not a crypto wallet or a bank, but people's genomic information is incredibly private and sensitive.&lt;/p&gt;
&lt;p&gt;My teammate's years of experience were distilled into prompts in English, then the AI agents automated everything.&lt;/p&gt;
&lt;p&gt;No Burp Suite. No Metasploit. No hand-crafted exploit scripts. Instead we had tea and pizza.&lt;/p&gt;
&lt;p&gt;I'm used to running AI on my laptop, often tethered to foundation model vendors.&lt;/p&gt;
&lt;p&gt;But I witnessed the future: that many agents working in parallel - they have to run in the cloud.&lt;/p&gt;
&lt;h2 id="agents-in-the-cloud"&gt;Agents in the Cloud&lt;/h2&gt;
&lt;p&gt;The abstraction pattern: hardware -&amp;gt; hosted infrastructure -&amp;gt; service -&amp;gt; intent-level runtime&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;evolution of running code:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;physical servers in an office &lt;em&gt;(that you maybe sometimes have to kick into rebooting)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;data centers ("Colo's") handling the physical stuff &lt;em&gt;(but you still start with bare metal and remotely install the OS, hopefully with a KVM-over-IP)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;virtual servers and the rise of VMware &lt;em&gt;(ok, managing dozens of VMs from templates, with snapshots, isn't so bad... until the over-provisioned host crashes)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;AWS leading the way in 2006 with "cloud instances" &lt;em&gt;(API for a server, cattle not pets! new scale, new problems like noisy neighbors and thousands of "machines")&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;PaaS (RIP Heroku) and then FaaS (AWS Lambda): "function as a service" = pure focus on the code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;evolution of running generative AI:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://papers.nips.cc/paper_files/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf"&gt;2 physical GPUs proved deep learning scales on Alexnet (Alex Krizhevsky and Ilya Sutskever)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Nvidia DGX: GPUs as an appliance aka purpose built hardware for AI, in your datacenter&lt;/li&gt;
&lt;li&gt;Renting GPUs from vendors like AWS (P2), Azure (N-series), Google Cloud (P100)&lt;/li&gt;
&lt;li&gt;"intelligence on demand" from foundation models like OpenAI and Anthropic, albeit a clunky interface&lt;/li&gt;
&lt;li&gt;Agents running "somewhere", taking human intent and just returning results&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;now for the fun problems with queuing, orchestration, observability, failure modes...&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="the-results"&gt;The Results&lt;/h2&gt;
&lt;p&gt;Those vulnerabilities were real, with evidence for each exploit. We (the humans) had to read the findings to ensure the most critical ones were real.&lt;/p&gt;
&lt;p&gt;Note the hackathon judges' reaction — awarding us the "scariest" prize — tells you the severity.&lt;/p&gt;
&lt;p&gt;Now the barrier to discovering serious vulnerabilities in critical open source infrastructure just dropped by an order of magnitude.&lt;/p&gt;
&lt;p&gt;What previously required deep domain expertise in specific codebases, custom tooling, and hours of manual analysis, can now be directed by someone who understands vulnerability classes and can write clear prompts.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The economics favor the attacker: they only have to find one exploitable path, defenders have to patch all of them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="hackathon-notes"&gt;Hackathon Notes&lt;/h1&gt;
&lt;p&gt;I love hackathons: the ideas and energy, teams coming together, the action of building. "ShipIt" days were highpoints for me at Atlassian.&lt;/p&gt;
&lt;p&gt;At Helix I evolved from participant to organizer of a company-wide 100+ person hackathon, including working closely with external guests (doctors/professors/professionals from the Medical University of South Carolina).&lt;/p&gt;
&lt;p&gt;Hats off to the NebulaFog organizers/hosts, they created an environment I rarely see at tech events: genuinely high-trust, positive, and kind. Lots of different tracks and challenges. A 13-year-old demo'd and he was amazing.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://nebulafog.ai/"&gt;https://nebulafog.ai/&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;No pitches. No spectators. A room full of people who ship, learning from each other, forming alliances, stress-testing what's possible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A perfect situation to connect and learn, I'll keep in touch with folks and be back next year!&lt;/p&gt;</content><category term="ai"/><category term="ai"/><category term="hackathon"/><category term="security"/></entry><entry><title>Pragmatic Summit - Nobody has the Answers on AI</title><link href="https://blog.john-pfeiffer.com/pragmatic-summit-nobody-has-the-answers-on-ai/" rel="alternate"/><published>2026-03-09T21:21:00-07:00</published><updated>2026-03-09T21:21:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2026-03-09:/pragmatic-summit-nobody-has-the-answers-on-ai/</id><summary type="html">
&lt;h1 id="nobody-has-the-answers"&gt;Nobody Has the Answers&lt;/h1&gt;
&lt;p&gt;I got my copy of "Refactoring" signed by both Martin Fowler and Kent Beck. That alone would have been worth the conference. What they shared in person at the Pragmatic Summit in SF was pure gold, Kent Beck put it bluntly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"At this moment, nobody knows …&lt;/p&gt;&lt;/blockquote&gt;</summary><content type="html">
&lt;h1 id="nobody-has-the-answers"&gt;Nobody Has the Answers&lt;/h1&gt;
&lt;p&gt;I got my copy of "Refactoring" signed by both Martin Fowler and Kent Beck. That alone would have been worth the conference. What they shared in person at the Pragmatic Summit in SF was pure gold, Kent Beck put it bluntly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"At this moment, nobody knows the answers to anything."&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="martin-fowler-and-kent-beck-walk-into-a-room-full-of-ai-startups"&gt;Martin Fowler and Kent Beck walk into a room full of AI startups&lt;/h2&gt;
&lt;p&gt;A decade ago &lt;a href="https://blog.john-pfeiffer.com/meeting-bjarne-stroustrup-creator-of-c-plus-plus-in-the-atlassian-dev-den/"&gt;I saw Bjarne Stroustrup in person, and he signed my C++ book.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;25 years after the &lt;strong&gt;&lt;a href="https://agilemanifesto.org/"&gt;Agile Manifesto&lt;/a&gt;&lt;/strong&gt;, two people who shaped the foundations of modern software engineering walked into a room full of AI-native startups and engineering leaders.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Up close to the wisdom of Martin Fowler and Kent Beck" src="../images/2026-02-11-pragmatic-summit-kent-beck-martin-fowler.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="My autographed Refactoring book" src="../images/2026-02-11-autographed-refactoring-book.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;If you haven't heard of Martin Fowler then you're in for a huge treat, so many incredible thoughts about software development (shared at &lt;a href="https://martinfowler.com/"&gt;https://martinfowler.com/&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;And Kent Beck's decades of wisdom (Extreme Programming, TDD, JUnit, etc.) are in books, substack posts, articles, podcasts, etc.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kentbeck.com/"&gt;https://kentbeck.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tidyfirst.substack.com/"&gt;https://tidyfirst.substack.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://newsletter.pragmaticengineer.com/p/tdd-ai-agents-and-coding-with-kent"&gt;https://newsletter.pragmaticengineer.com/p/tdd-ai-agents-and-coding-with-kent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="paradigm-shift"&gt;Paradigm shift&lt;/h3&gt;
&lt;p&gt;There was a huge historical shift into the era of personal computing and the internet. And for 25 years, experienced engineers have had a playbook:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Too many bugs? Here's how you write tests.&lt;/li&gt;
&lt;li&gt;Can't write tests? Here's how you design and architect your software.&lt;/li&gt;
&lt;li&gt;Go look up a (coding/architecture) pattern in the book&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now with LLMs it has become a completely different software development lifecycle and new tech stack; a whole different set of skills.&lt;/p&gt;
&lt;p&gt;Kent Beck's poignant appeal (for geeks who love code): It doesn't feel safe right now.&lt;/p&gt;
&lt;p&gt;His antidote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"You're just as smart as everybody else because you're just as ignorant as everybody else."&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the age of AI hype fatigue and &lt;a href="https://lucumr.pocoo.org/2026/1/18/agent-psychosis/"&gt;AI agent psychosis&lt;/a&gt;, I find this oddly reassuring.&lt;/p&gt;
&lt;h1 id="the-craft-still-matters"&gt;The Craft Still Matters&lt;/h1&gt;
&lt;p&gt;The most energizing takeaway from the session was that good engineering practices aren't becoming obsolete, they're becoming more important.&lt;/p&gt;
&lt;p&gt;Fowler described a repeatedly heard pattern: well-modularized code makes it easier for AI agents to work effectively. Good test suites help agents just as much as they help humans. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"The Venn diagram of developer experience and agent experience is a circle."&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One colleague told Beck that 20 years of pushing TDD was paying off now because...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;when you have "a big powerful genie," you need to know how to verify it's doing the right thing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When you step away from hype around AGI and autonomous agents and use first principles thinking, this really makes sense.&lt;/p&gt;
&lt;p&gt;Software engineering is not just programming: regardless of human or AI agent, it's based on foundations like complexity/modularity, design/documentation, tests, security, observability, etc.&lt;/p&gt;
&lt;h2 id="learn-the-tools"&gt;Learn the Tools&lt;/h2&gt;
&lt;p&gt;Martin Fowler contended there's been snake oil in software for a long time, and "agile certification factories" (coaches, titles, etc).&lt;/p&gt;
&lt;p&gt;His early experience with AI code completion in Emacs was bad enough that he nearly wrote it off entirely. Most completions were garbage. &lt;/p&gt;
&lt;p&gt;But he kept probing, found &lt;a href="https://simonwillison.net/"&gt;Simon Willison's blog&lt;/a&gt;, and saw someone continuously experimenting, with curiosity, learning the new tools; willing to show both the good and bad, to say "I don't know."&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Be skeptical about your skepticism&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Kent Beck's version: "What's the least I can do to validate, to my own satisfaction, whether this claim is true or not?"&lt;/p&gt;
&lt;p&gt;That experimental mindset has become, in his words, "a thousand times more valuable" in the last year. The answers change week to week. A tool that fails on Monday might work on Thursday. You can't have &lt;em&gt;the&lt;/em&gt; answer. You can only have a practice of finding out.&lt;/p&gt;
&lt;h2 id="sdlc-ai"&gt;SDLC + AI&lt;/h2&gt;
&lt;p&gt;Previously a generation (or two) of developers used to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;read requirements (PRD)&lt;/li&gt;
&lt;li&gt;systems design (hopefully?)&lt;/li&gt;
&lt;li&gt;write code (usually in an IDE)&lt;/li&gt;
&lt;li&gt;run tests (right?)&lt;/li&gt;
&lt;li&gt;submit pull request&lt;/li&gt;
&lt;li&gt;continuous integration&lt;/li&gt;
&lt;li&gt;continuous deployment&lt;/li&gt;
&lt;li&gt;smoketests&lt;/li&gt;
&lt;li&gt;observability and monitoring&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;given the rate of change I hope this doesn't become obsolete in a month ;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Emerging SDLC With AI&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;deep research agent creates a report on the domain&lt;/li&gt;
&lt;li&gt;LLMs "architect persona" brainstorms and rubber duck options&lt;/li&gt;
&lt;li&gt;Using agents with Skills to plan and decompose the work&lt;/li&gt;
&lt;li&gt;A carefully crafted prompt kicks off a series of agents in parallel&lt;/li&gt;
&lt;li&gt;Human reviews and refines Test Cases and Guardrails, defines verifiability&lt;/li&gt;
&lt;li&gt;Human creates Evals for the critical or non-deterministic portions of the workflow/service/product&lt;/li&gt;
&lt;li&gt;A series of specialized agents (security, design, etc) review the agent produced artifacts&lt;/li&gt;
&lt;li&gt;CI/CD and automated tests run; agents autonomously attempt fixes for anything failing&lt;/li&gt;
&lt;li&gt;Human reviews the proposed output, any test failure escalations, eval scores&lt;/li&gt;
&lt;li&gt;Production deployment triggers extra LLM traces and privacy protecting prompt logging&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="shift-focus-to-the-system"&gt;Shift Focus to the System&lt;/h1&gt;
&lt;p&gt;Kent Beck's personal reflection resonated: the deep satisfaction of refactoring code and taking a messy file, making tiny safe steps, watching clarity emerge. Then he said plainly: &lt;strong&gt;"I can't do that anymore."&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Previously software engineers who were great at managing the code line-by-line and the attendant complexity, like using Tidy First or TDD, they had huge leverage.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;There was a time when hand-writing optimized assembly was the high-leverage skill, then compilers made that less valuable.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The leverage in the craft is now understanding the &lt;strong&gt;domain&lt;/strong&gt; and its connection to the software, the system. Not optimizing the code in a specific function or file.&lt;/p&gt;
&lt;p&gt;This makes sense to me: so many complaints about microservices are due to an absence of &lt;a href="https://se-radio.net/2015/05/se-radio-episode-226-eric-evans-on-domain-driven-design-at-10-years/"&gt;Domain Driven Design&lt;/a&gt;, and missing the "value stream" — what are we building, for whom, and how does this piece connect to the whole?&lt;/p&gt;
&lt;p&gt;Human intent: what was the system designed to do?&lt;/p&gt;
&lt;h2 id="measuring-success"&gt;Measuring Success&lt;/h2&gt;
&lt;p&gt;Kent Beck's right that companies have very high hopes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Expecting better, faster, cheaper&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;and his experience has been that inside a company, the incentives are so misaligned with actually achieving that&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;AI can generate code fast, but &lt;em&gt;human intent and system understanding&lt;/em&gt; are what drive it toward quality outputs and remarkable outcomes.&lt;/p&gt;
&lt;p&gt;Kent Beck pointed out: carpentry didn't end because of circular saws (or nail guns). People just got more powerful tools.&lt;/p&gt;
&lt;p&gt;In my opinion, measuring Lines of Code or Pull Requests is a poor proxy that's not correlated with the full outcome.&lt;/p&gt;
&lt;h3 id="burnout"&gt;Burnout&lt;/h3&gt;
&lt;p&gt;And we've always known about software and burnout; clearly people coding need breaks because overwork creates bad ideas and negative value.&lt;/p&gt;
&lt;p&gt;I know that when I'm in the zone and stay up well past midnight coding... then the morning comes. After sleeping, reviewing with fresh eyes (and brain) reveals so many obvious bugs and mistakes in the logic.&lt;/p&gt;
&lt;p&gt;I also know from more than a decade of managing engineers that product release "death marches" and working nights and weekends degrades a team beyond repair, people are cranky and can no longer function together.&lt;/p&gt;
&lt;p&gt;Once I even had to explain to the C-Suite: fine, we'll work weekends, but what days are left when that's no longer enough?&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Seeing Kent Beck and Martin Fowler in person reinforced that really wise thought leaders in the industry also like corny jokes and find conference chairs uncomfortable.&lt;/p&gt;
&lt;p&gt;Clearly they are beacons for quality craftsmanship, and their message was "experiment and learn to use this new tool well".&lt;/p&gt;
&lt;p&gt;I also took away that we need to measure the impacts of the "new way" on outcomes we care about: users/customers, revenue... and the people building the software.&lt;/p&gt;
&lt;h1 id="resources"&gt;Resources&lt;/h1&gt;
&lt;p&gt;The conference: &lt;strong&gt;&lt;a href="https://www.pragmaticsummit.com/"&gt;https://www.pragmaticsummit.com/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The specific session, and the amazing Pragmatic Summit 2026 playlist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=CZs8J1ZD0CE&amp;amp;list=PLzwJJv8h-icjtYA5oHmc7g6qU1t4OqDqb&amp;amp;index=8"&gt;https://www.youtube.com/watch?v=CZs8J1ZD0CE&amp;amp;list=PLzwJJv8h-icjtYA5oHmc7g6qU1t4OqDqb&amp;amp;index=8&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="addendum"&gt;Addendum&lt;/h2&gt;
&lt;p&gt;I missed seeing Simon Willison &lt;em&gt;(co-creator of Django)&lt;/em&gt; in person but did get to watch the video of his session, some important takeways:&lt;/p&gt;
&lt;p&gt;His personal evolution from Human writes all the code -&amp;gt; LLM writes some code -&amp;gt; LLM writes all the code and Human reviews the code -&amp;gt; Human does not read the code at all, focus on creating a testable goal&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Big organizations had a team create a service and API... and you just trust them until something goes wrong&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;write prompts on your phone and the LLM writes the code&lt;/li&gt;
&lt;li&gt;prompt the LLM "use red/green TDD"; this focuses to minimal code writing, and improves quality and LLMs make writing tests almost free&lt;/li&gt;
&lt;li&gt;full time code reviewer is exhausting - humans can't keep up with agents code output&lt;/li&gt;
&lt;li&gt;think really hard how the agent proves what's been created really works&lt;/li&gt;
&lt;li&gt;be creative to ask for tests how it really works in the real world (unit test of code &amp;lt; curl the webserver)&lt;/li&gt;
&lt;li&gt;detailed specs and tests allow for very easy reverse-engineering and building by AI agents&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Avoid the "Lethal Trifecta" &lt;a href="https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/"&gt;https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Have lots of fun, experiment with everything =)&lt;/p&gt;</content><category term="leadership"/><category term="ai"/><category term="career"/><category term="engineering"/><category term="management"/><category term="leadership"/><category term="navigating change"/></entry><entry><title>Intro to MCP - give your LLM tools with model context protocol</title><link href="https://blog.john-pfeiffer.com/intro-to-mcp-give-your-llm-tools-with-model-context-protocol/" rel="alternate"/><published>2025-07-27T19:20:00-07:00</published><updated>2025-07-27T19:20:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2025-07-27:/intro-to-mcp-give-your-llm-tools-with-model-context-protocol/</id><summary type="html">
&lt;h1 id="why-is-weather-so-hard"&gt;Why is weather so hard?&lt;/h1&gt;
&lt;p&gt;Let's start with a problem...&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(rather than technology looking for a problem ;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Getting the weather can be frustrating&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;which of the 6+ websites would you choose?&lt;/li&gt;
&lt;li&gt;how many annoying ads?&lt;/li&gt;
&lt;li&gt;why does one show the city first, another shows daily temperature, and a third jumps …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">
&lt;h1 id="why-is-weather-so-hard"&gt;Why is weather so hard?&lt;/h1&gt;
&lt;p&gt;Let's start with a problem...&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(rather than technology looking for a problem ;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Getting the weather can be frustrating&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;which of the 6+ websites would you choose?&lt;/li&gt;
&lt;li&gt;how many annoying ads?&lt;/li&gt;
&lt;li&gt;why does one show the city first, another shows daily temperature, and a third jumps to the week ahead?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It can take many clicks to view a specific city that you're interested in...&lt;/p&gt;
&lt;p&gt;And many prompts to "sign up as a user" or "subscribe aka pay money" to have your view of the weather customized&lt;/p&gt;
&lt;p&gt;So pay for a weather app - that still doesn't do what you want?&lt;/p&gt;
&lt;p&gt;Or find a weather API, write your own app/website, just the way you want it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"5 day forecast of days and nights - temperatures in both F and C&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="how-useful-is-your-toilet-without-the-plumbing"&gt;How useful is your toilet without the plumbing?&lt;/h1&gt;
&lt;p&gt;LLMs (Large Language Models) excel at natural language processing (NLP) - like asking the computer about the weather (typos and all!)&lt;/p&gt;
&lt;p&gt;But the LLM doesn't have access to up to date info (weather, prices) or private data (emails, databases).&lt;/p&gt;
&lt;p&gt;Model Context Protocol is a way to solve multiple things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A user would like the LLM to utilize newer or specific info&lt;/li&gt;
&lt;li&gt;Each LLM vendor has a "custom way to configure"&lt;/li&gt;
&lt;li&gt;With multiple LLM applications running locally... and you have a lot of tools&lt;/li&gt;
&lt;li&gt;Organizations want a centralized way to control access to tools and (potentially private) information&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Therefore this is an "N x M" problem: N (LLM) applications for M tools&lt;/p&gt;
&lt;p&gt;The grand vision: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;LLM vendors have a standard way to give users more leverage/capabilities&lt;/li&gt;
&lt;li&gt;Developers can build these "MCP Servers" once - and support every LLM product&lt;/li&gt;
&lt;li&gt;Organizations can centralize and manage all the LLMs accessing a given capability or dataset&lt;/li&gt;
&lt;li&gt;Users have far more powerful tools with very little setup&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="pre-requisites"&gt;Pre-Requisites&lt;/h1&gt;
&lt;p&gt;Install the python dependency manager&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl -LsSf https://astral.sh/uv/install.sh | sh&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Or for MacOS &lt;code&gt;brew install uv&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;background: &lt;a href="https://docs.astral.sh/uv/"&gt;https://docs.astral.sh/uv/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Create a default project from a built in template with uv:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;uv init mcp-server-demo&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This creates a very minimal package (dependency) management configuration file
- &lt;a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/"&gt;https://packaging.python.org/en/latest/guides/writing-pyproject-toml/&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;[project]&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"mcp-server-demo"&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Add your description here"&lt;/span&gt;
&lt;span class="na"&gt;readme&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"README.md"&lt;/span&gt;
&lt;span class="na"&gt;requires-python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;=3.13"&lt;/span&gt;
&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;you can ignore or remove the extraneous .gitignore, .git, README.md files&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Remove (delete) main.py as it is extraneous for this project.&lt;/p&gt;
&lt;p&gt;Next create a python virtual environment and activate it, and install dependencies:
- &lt;a href="https://docs.python.org/3/library/venv.html"&gt;https://docs.python.org/3/library/venv.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cd mcp-server-demo&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;uv venv&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;source .venv/bin/activate&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;uv add "mcp[cli]" httpx&lt;/code&gt;&lt;/p&gt;
&lt;h1 id="the-code"&gt;The code&lt;/h1&gt;
&lt;p&gt;Use a coding editor to create weather.py that queries the national weather service and provides the MCP hook:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;httpx&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mcp.server.fastmcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;

&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"weather"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;NWS_API_BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://api.weather.gov"&lt;/span&gt;
&lt;span class="n"&gt;USER_AGENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"weather-app/1.0"&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_nws_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"User-Agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;USER_AGENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"application/geo+json"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;30.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

&lt;span class="nd"&gt;@mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_forecast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# First get the forecast grid endpoint&lt;/span&gt;
    &lt;span class="n"&gt;points_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;NWS_API_BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/points/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;points_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;make_nws_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;points_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;points_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Unable to fetch forecast data for this location."&lt;/span&gt;

    &lt;span class="c1"&gt;# Get the forecast URL from the points response&lt;/span&gt;
    &lt;span class="n"&gt;forecast_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"forecast"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;forecast_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;make_nws_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forecast_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;forecast_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Unable to fetch detailed forecast."&lt;/span&gt;

    &lt;span class="c1"&gt;# Format the periods into a readable forecast&lt;/span&gt;
    &lt;span class="n"&gt;periods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forecast_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"periods"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;forecasts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;period&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;periods&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;  &lt;span class="c1"&gt;# Only show next 5 periods&lt;/span&gt;
        &lt;span class="n"&gt;forecast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;
&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;
&lt;span class="s2"&gt;Temperature: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'temperature'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;°&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'temperatureUnit'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;Wind: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'windSpeed'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'windDirection'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;Forecast: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'detailedForecast'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;forecasts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forecast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;---&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forecasts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Initialize and run the server&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'stdio'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The second function in the file specifically creates the "MCP Tool"&lt;/p&gt;
&lt;p&gt;The end of the file is the classic python main to have this run as a server&lt;/p&gt;
&lt;h2 id="one-last-config-pyprojecttoml"&gt;one last config pyproject.toml&lt;/h2&gt;
&lt;p&gt;Also, modify &lt;code&gt;pyproject.toml&lt;/code&gt; to ensure it connects the weather.py and main:&lt;/p&gt;
&lt;p&gt;[project.scripts]&lt;/p&gt;
&lt;p&gt;weather = "weather:main"&lt;/p&gt;
&lt;p&gt;example of the final &lt;strong&gt;pyproject.toml&lt;/strong&gt; file&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;[project]&lt;/span&gt;
&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp-server-demo"&lt;/span&gt;
&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Add your description here"&lt;/span&gt;
&lt;span class="n"&gt;readme&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"README.md"&lt;/span&gt;
&lt;span class="n"&gt;requires-python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;=3.13"&lt;/span&gt;
&lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"httpx&amp;gt;=0.28.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"mcp[cli]&amp;gt;=1.17.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;[project.scripts]&lt;/span&gt;
&lt;span class="n"&gt;weather&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"weather:main"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id="configure-claude-to-use-your-new-weather-mcp"&gt;Configure Claude to use your new Weather MCP&lt;/h1&gt;
&lt;p&gt;The following tells your LLM application (i.e. Claude for Desktop)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is an MCP server named “weather”&lt;/li&gt;
&lt;li&gt;launch it by running uv --directory /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather run weather.py&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Modify to add the JSON to the file ~/Library/Application\ Support/Claude/claude_desktop_config.json&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"weather"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uv"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"--directory"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"weather.py"&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Restart your LLM application (claude for desktkop)&lt;/p&gt;
&lt;p&gt;You should be able to see it in the UI: Settings -&amp;gt; Developer &lt;/p&gt;
&lt;p&gt;"your new MCP server"&lt;/p&gt;
&lt;h1 id="a-better-response-about-the-weather"&gt;A better response about the weather&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;(When the LLM application "aka MCP host" starts, it also launches your MCP server as a subprocess)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You (the user) have your LLM running locally and ask:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What's the weather in San Francisco?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The LLM decides it can use the "weather" tool to help answer the question&lt;/p&gt;
&lt;p&gt;&lt;em&gt;It actually prompts at first to to approve the permissions:&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Claude wants to use Get Forecast from weather&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The LLM has geographic data in its training and can use it's "super distilled storage" to convert a city name to coordinates&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;Reques&lt;/span&gt;&lt;span class="kc"&gt;t&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;`la&lt;/span&gt;&lt;span class="kc"&gt;t&lt;/span&gt;&lt;span class="err"&gt;i&lt;/span&gt;&lt;span class="kc"&gt;tu&lt;/span&gt;&lt;span class="err"&gt;de`&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;37.7749&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;`lo&lt;/span&gt;&lt;span class="kc"&gt;n&lt;/span&gt;&lt;span class="err"&gt;gi&lt;/span&gt;&lt;span class="kc"&gt;tu&lt;/span&gt;&lt;span class="err"&gt;de`&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-122.4194&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The LLM (MCP Host) is the client which communicates via JSON-RPC over stdin/stdout with the "MCP weather server"&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The (local) MCP weather server makes HTTP requests to the National Weather Service API&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The forecast data (JSON) is returned from the MCP Server to the "MCP Host"&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The LLM (MCP Host) incorporates and reformats the JSON into a natural language response&lt;/p&gt;
&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/docs/develop/build-server"&gt;https://modelcontextprotocol.io/docs/develop/build-server&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/modelcontextprotocol"&gt;https://github.com/modelcontextprotocol&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="last-thoughts"&gt;Last thoughts&lt;/h1&gt;
&lt;p&gt;Adding tools to an LLM further extends the leverage already available with amazing default NLP that has fact sets and token generation.&lt;/p&gt;
&lt;p&gt;Capabilities like "current weather" can be added in a straightforward manner.&lt;/p&gt;
&lt;p&gt;MCP unlocks current data, private data, specialized tools, or even running a machine in the physical world - bridging the many fragmented and distinct APIs and interfaces&lt;/p&gt;
&lt;h1 id="bonus-content"&gt;Bonus Content&lt;/h1&gt;
&lt;p&gt;You can directly see what the National Weather Service API returns&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl https://api.weather.gov/points/37.780,-122.420&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;interestingly lat,lon are converted into "grids" which each local forecast office uses, so the JSON returns "Daly City" or "Sausalito"&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;https://forecast.weather.gov/MapClick.php?lon=-122.4192&amp;amp;lat=37.7793&lt;/p&gt;
&lt;p&gt;Send JSON RPC in bash&lt;/p&gt;
&lt;p&gt;&lt;code&gt;uv run weather.py &amp;lt;&amp;lt; 'EOF'&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;heredoc&amp;gt; &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"initialize"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;"protocolVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-11-05"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;"capabilities"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;"clientInfo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;EOF&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A response&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nt"&gt;"protocolVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2024-11-05"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;"capabilities"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nt"&gt;"experimental"&lt;/span&gt;&lt;span class="p"&gt;:{},&lt;/span&gt;&lt;span class="nt"&gt;"prompts"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nt"&gt;"listChanged"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="nt"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nt"&gt;"subscribe"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;"listChanged"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="nt"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nt"&gt;"listChanged"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="nt"&gt;"serverInfo"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nt"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"weather"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"1.17.0"&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="ai"/><category term="ai"/><category term="llm"/><category term="mcp"/></entry><entry><title>Maximum leverage and Minimum Ops with Google Cloud Run and the Jules Coding Agent</title><link href="https://blog.john-pfeiffer.com/maximum-leverage-and-minimum-ops-with-google-cloud-run-and-the-jules-coding-agent/" rel="alternate"/><published>2025-06-07T06:07:00-07:00</published><updated>2025-06-07T06:07:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2025-06-07:/maximum-leverage-and-minimum-ops-with-google-cloud-run-and-the-jules-coding-agent/</id><summary type="html">
&lt;h1 id="why-software-agents-are-eating-the-world"&gt;Why Software Agents are Eating the World&lt;/h1&gt;
&lt;p&gt;Software scales to infinity and prints money the whole way.&lt;/p&gt;
&lt;p&gt;Once the cost of distribution went to zero the dominant constraint on growth stopped being infrastructure and became people: how fast could humans design, write, deploy (and maintain!) software.&lt;/p&gt;
&lt;p&gt;Over 30+ years, to …&lt;/p&gt;</summary><content type="html">
&lt;h1 id="why-software-agents-are-eating-the-world"&gt;Why Software Agents are Eating the World&lt;/h1&gt;
&lt;p&gt;Software scales to infinity and prints money the whole way.&lt;/p&gt;
&lt;p&gt;Once the cost of distribution went to zero the dominant constraint on growth stopped being infrastructure and became people: how fast could humans design, write, deploy (and maintain!) software.&lt;/p&gt;
&lt;p&gt;Over 30+ years, to get increasing leverage (less humans!) and take advantage of economic specialization...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Self-hosted physical servers became leasing space and energy/cooling with colocation&lt;/li&gt;
&lt;li&gt;"Colos" physical servers became leasing virtual machines&lt;/li&gt;
&lt;li&gt;Overprovisioned VMs became renting "cloud servers"&lt;/li&gt;
&lt;li&gt;Cloud services became pay by the millisecond "serverless"&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A remaining bottleneck: humans to write and maintain the code.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;LLM Coding Agents is "software that writes software"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="personal-goal-of-efficiency"&gt;Personal Goal of Efficiency&lt;/h2&gt;
&lt;p&gt;Physical wires and failing components, maintaining operating systems (security!), copying files and manually checking if the software even works at all... and even yak shaving down the rabbit hole of code syntax, dependency updates, edge cases, and content typos&lt;/p&gt;
&lt;p&gt;Over the years I have leveraged increasing levels of abstractions for provisioning the Environment and Infrastructure (IaaC), and for publishing Content/Code (git to production), all to increase cognitive focus and impact.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;see my previous articles on Vagrant, Packer, Terraform, Docker, Static websites, Heroku, AWS, Google App Engine, and AWS Lambdas&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Try it for yourself: tell an LLM what you want and asynchronously it comes back (like any remote virtual colleague) with the changes - approve and voila it's live in seconds.&lt;/p&gt;
&lt;h1 id="another-day-another-cloud"&gt;Another day another cloud&lt;/h1&gt;
&lt;p&gt;The goal is to give the LLM agent Jules instructions in english and go from git to production in seconds.&lt;/p&gt;
&lt;p&gt;Previously I have used Google App Engine but it seems to have been superseded by Google Cloud Run (and hopefully less byzantine IAM permissions and "Artifact Registry" costs)&lt;/p&gt;
&lt;p&gt;Google Cloud Run "free tier" is still confusing to estimate and calculate (charged by the millisecond) but should "just run".&lt;/p&gt;
&lt;p&gt;So here's how to setup a golang web server automatically built and deployed from each GitHub commit.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: apparently Cloud Run also uses Artifact Registry to automatically leverage a Docker image that presumably if deployed enough times could go beyond the free tier :(&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="different-day-same-code"&gt;Different day same Code&lt;/h2&gt;
&lt;p&gt;If the web server starts automatically on port 8080 then it is "Cloud Run Compatible"&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/johnpfeiffer/aws-go-lambda"&gt;https://github.com/johnpfeiffer/aws-go-lambda&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Their official example https://github.com/GoogleCloudPlatform/cloud-run-microservice-template-go&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="google-is-project-centric"&gt;Google is project-centric&lt;/h2&gt;
&lt;p&gt;Then navigate to a project (or make a new one)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://console.cloud.google.com/run/overview?project=go-cache"&gt;https://console.cloud.google.com/run/overview?project=go-cache&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;"Deploy a web service" -&amp;gt; Connect repository&lt;/p&gt;
&lt;h2 id="configuration"&gt;Configuration&lt;/h2&gt;
&lt;p&gt;GitHub: "Continuously deploy from a repository (source or function)"
&lt;em&gt;(this works for languages supported by Google)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Cloud Build: Can deploy from GitHub repositories.&lt;/p&gt;
&lt;p&gt;"Set up with Cloud Build" (button)&lt;/p&gt;
&lt;p&gt;Source repository: GitHub&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Currently not authenticated, Authenticate&lt;blockquote&gt;
&lt;p&gt;You will be asked to authorize the Google Cloud Build GitHub App to access your GitHub Account to proceed. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;creates a pop up (check your blockers!): "Authorize Google Cloud Build"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Repository: Manage connected repositories&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;creates a pop up to GitHub (simplified integration and authentication/authorization)&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Only select repositories (choose from the dropdown)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;legalese checkbox&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Build Configuration&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Branch: ^main$&lt;/p&gt;
&lt;p&gt;Build Type: Go (or Node.js, Python, Java, .NET, Ruby, PHP)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;nothing special for Build context, Entrypoint, or Function target (The name of the exported function to be invoked, leave blank if the source repository is a web server.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Service name: go-cache&lt;/p&gt;
&lt;p&gt;Region: us-west2 &lt;em&gt;(tier2 is cheaper than tier1)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Authentication: Public &lt;em&gt;(unless you have something specific in mind)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Billing: Request-based, Charged only when processing requests. CPU is limited outside of requests.&lt;/p&gt;
&lt;p&gt;Service scaling: Minimum number of instances = 0 , Maximum number of instances = 1&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;very important to set these values to 0 and 1 to stay in the free tier&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;"Edit Container" has the opportunity to change things or add a variable/secret - ignore for now&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create (button)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.cloud.google.com/run/docs/continuous-deployment#existing-service"&gt;https://docs.cloud.google.com/run/docs/continuous-deployment#existing-service&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="checking-on-builds"&gt;Checking on Builds&lt;/h2&gt;
&lt;p&gt;When builds are occurring (or failing) &lt;/p&gt;
&lt;p&gt;&lt;a href="https://console.cloud.google.com/cloud-build/"&gt;https://console.cloud.google.com/cloud-build/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="cleanup-artifact-registry"&gt;Cleanup Artifact Registry&lt;/h2&gt;
&lt;p&gt;You can search and find amid the 100s of services the one that sometimes invisibly goes over a free tier...&lt;/p&gt;
&lt;p&gt;&lt;a href="https://console.cloud.google.com/artifacts/"&gt;https://console.cloud.google.com/artifacts/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Click on "cloud-run-source-deploy" and click in until you get to something like "Digests for aws-go-lambda/go-cache"&lt;/p&gt;
&lt;p&gt;Choose an old deploy as a checkbox and then at the top click Delete&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Are you sure you want to delete...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;hopefully this keeps me under the free tier limit&lt;/em&gt;&lt;/p&gt;
&lt;h1 id="agentic-coding-with-google-jules"&gt;Agentic Coding with Google Jules&lt;/h1&gt;
&lt;p&gt;Google is making their LLM offerings like Gemini and Jules free up to a certain limit - an excellent opportunity to experiment. &lt;em&gt;caveat emptor that this may change over time&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Google "Gemini" (https://gemini.google.com) is the LLM chat application, useful for questions about technology terms, architecture options, code snippets, and troubleshooting, etc.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;...you have encountered the very real friction of Google Cloud's IAM system firsthand. What should feel like a single, simple action often requires a complex combination of permissions... the single App Engine Deployer role is not sufficient for a complete developer workflow, and it does not cover administrative tasks or broader access needs&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Getting clarity in your own thinking is important before handing work off to Jules.&lt;/p&gt;
&lt;p&gt;"Agentic Coding" is where the LLM agent takes instructions in english and modifies the code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://jules.google.com/"&gt;https://jules.google.com/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Google account as before, Sign in, accept all the terms and conditions of this experimental AI software&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Has a purple octopus logo&lt;/p&gt;
&lt;p&gt;Choose a Repo from the dropdown (top center) -&amp;gt; "Configure repo access"&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;opens a new tab (window?) in GitHub for "Google Labs Jules"&lt;/li&gt;
&lt;li&gt;Repository access: Only select repositories (choose the ones the agent can work on)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;"Save" (button)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Successfully authenticated with GitHub!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/settings/installations"&gt;https://github.com/settings/installations&lt;/a&gt; is where GitHub lists integrated Applications&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Choose the repo (i.e. johnpfeiffer/aws-go-lambda)&lt;/p&gt;
&lt;p&gt;Give Jules the instructions in the text box (not on the bottom right you can choose a mode like "interactive plan" or "review" - or just &lt;strong&gt;"Start"&lt;/strong&gt;)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;These tasks run in an isolated sandbox hosted by Google&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="watching-an-agentic-task-in-progress"&gt;Watching an agentic task in progress&lt;/h2&gt;
&lt;p&gt;On the left (expand/collapse) side panel you can see how many "daily sessions" you have left.&lt;/p&gt;
&lt;p&gt;Clicking on a session gives you all the details:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how the agent broke the instructions down into a plan&lt;/li&gt;
&lt;li&gt;it runs &lt;code&gt;go test -v&lt;/code&gt; on its own&lt;/li&gt;
&lt;li&gt;sends a (draft) pull request to the code repository&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;there is a "View PR" button&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/johnpfeiffer/aws-go-lambda/pull/1"&gt;https://github.com/johnpfeiffer/aws-go-lambda/pull/1&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="iterating-on-the-pull-request"&gt;Iterating on the Pull Request&lt;/h2&gt;
&lt;p&gt;Leaving a comment on the pull request will feedback to Jules (yay for all the permissions) who then automatically resumes the session.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I have received the PR comments and am processing them.&lt;/p&gt;
&lt;p&gt;Ready for review &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;the usual GitHub pull request merge process&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="gotchas-with-agents"&gt;Gotchas with Agents&lt;/h2&gt;
&lt;p&gt;All of this leverage shifts the bottleneck from "programmer" to "good ideas and ability to review changes". Yet this new technology brings new challenges...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the LLM sounds confident even when wrong or has mistakes&lt;/li&gt;
&lt;li&gt;often imports random libraries or calls external APIs&lt;/li&gt;
&lt;li&gt;deletes tests&lt;/li&gt;
&lt;li&gt;modifies code and says "done"... but the code does not work, is not done&lt;/li&gt;
&lt;li&gt;mediocre code quality that is either one large ball of mud or a tangle of tiny functions&lt;/li&gt;
&lt;li&gt;unnecessarily over-engineered code and abstractions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mitigations&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;read and review critically&lt;/li&gt;
&lt;li&gt;have specifications, documentation, and anchors&lt;/li&gt;
&lt;li&gt;tell it build as simple as possible and not to call external APIs&lt;/li&gt;
&lt;li&gt;give it boundary instructions: that it cannot modify or delete tests&lt;/li&gt;
&lt;li&gt;give it examples of expected input and output&lt;/li&gt;
&lt;li&gt;give it explicit instructions on the level of modularity ("make this 3 functions")&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-does-it-all-mean"&gt;What does it all mean&lt;/h2&gt;
&lt;p&gt;Even with software writing software there is actually an increasing need for human intent, and managing of complexity (including ensuring generated code is readable, modular, testable, etc.)&lt;/p&gt;</content><category term="build-CI-CD-devops"/><category term="github"/><category term="google cloud run"/><category term="ci"/><category term="cd"/><category term="deployment"/><category term="ai"/><category term="agent"/></entry><entry><title>Building a desktop app with Golang and Wails</title><link href="https://blog.john-pfeiffer.com/building-a-desktop-app-with-golang-and-wails/" rel="alternate"/><published>2025-04-06T10:10:00-07:00</published><updated>2025-04-06T10:10:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2025-04-06:/building-a-desktop-app-with-golang-and-wails/</id><summary type="html">
&lt;p&gt;tldr: a Golang native desktop app using Wails (Go backend + React frontend = single binary)&lt;/p&gt;
&lt;h1 id="why-this-tech-stack-and-why-a-desktop-app"&gt;Why this tech stack and why a desktop app?&lt;/h1&gt;
&lt;p&gt;Desktop apps provide privacy for the persistence layer, and bypass the "where is it hosted" challenges.&lt;/p&gt;
&lt;p&gt;This is a simple and practical way to leverage some great …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;tldr: a Golang native desktop app using Wails (Go backend + React frontend = single binary)&lt;/p&gt;
&lt;h1 id="why-this-tech-stack-and-why-a-desktop-app"&gt;Why this tech stack and why a desktop app?&lt;/h1&gt;
&lt;p&gt;Desktop apps provide privacy for the persistence layer, and bypass the "where is it hosted" challenges.&lt;/p&gt;
&lt;p&gt;This is a simple and practical way to leverage some great backend + frontend technologies.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you need a more basic react intro see my previous post &lt;a href="https://blog.john-pfeiffer.com/react-javascript-intro/"&gt;https://blog.john-pfeiffer.com/react-javascript-intro/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The key: Go methods with exported (capitalized) names get auto generated as JavaScript/TypeScript bindings. You call Go functions from React as if they were local async functions.&lt;/p&gt;
&lt;h2 id="why-wails"&gt;Why Wails&lt;/h2&gt;
&lt;p&gt;Wails uses the operating system's native webview (WebKit on macOS, WebView2 on Windows, WebKitGTK on Linux) so the resulting binary is ~10MB.&lt;/p&gt;
&lt;p&gt;As a comparison to a very popular technology, electron bundles an entire Chromium browser into your app - which is why a "hello world" Electron app is ~150MB and idles at 100MB+ of RAM.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;┌───────────────────────────────────────────────────────────┐
│  WAILS APPLICATION                                        │
│                                                           │
│   Go Backend              React Frontend                  │
│   ┌───────────┐           ┌──────────────────┐            │
│   │ app.go    │◄─────────►│ App.tsx          │            │
│   │ (structs  │  bindings │ (components,     │            │
│   │  &amp;amp; methods│  ───────► │  hooks, UI)      │            │
│   │  &amp;amp; stdlib)│           │                  │            │
│   └───────────┘           └──────────────────┘            │
│         │                        │                        │
│         └──────────┬──────────────┘                         │
│                    ▼                                        │
│         ┌────────────────────┐                             │
│         │  Native WebView    │                             │
│         │  (WebKit on macOS) │                              │
│         └────────────────────┘                              │
│                    │                                        │
│                    ▼                                        │
│            Single Binary (~10MB)                            │
└─────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="why-golang"&gt;Why golang&lt;/h2&gt;
&lt;p&gt;Golang is a remarkably performant language but it still automatically handles memory management (with garbage collection)&lt;/p&gt;
&lt;p&gt;Golang builds to a single binary&lt;/p&gt;
&lt;h1 id="pre-requisites"&gt;Pre-requisites&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;the assumption here is MacOS...&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Golang: https://go.dev/doc/install&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;brew install golang&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;which go; go version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;echo 'export PATH="$PATH:$(go env GOPATH)/bin"' &amp;gt;&amp;gt; ~/.zshrc&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;because "GOPATH/bin" is the default destination for &lt;code&gt;go install&lt;/code&gt; - now making tools runnable&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;NPM: https://nodejs.org/en/download/
- &lt;code&gt;brew install node&lt;/code&gt;
- &lt;code&gt;which npm; npm --version; node --version&lt;/code&gt; &lt;/p&gt;
&lt;h2 id="installing-wails"&gt;Installing Wails&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Wails framework: https://wails.io/docs/gettingstarted/installation/&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;go install github.com/wailsapp/wails/v2/cmd/wails@latest&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ls $(go env GOPATH)/bin&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wails version&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Troubleshooting Wails&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wails doctor&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This checks Go version, node/npm, and platform-specific dependencies (on macOS you need Xcode command line tools)&lt;/p&gt;
&lt;h1 id="simplest-start-with-wails"&gt;Simplest start with Wails&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;wails init -l&lt;/code&gt; &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;list the different types of wails default project layouts&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Plain HTML/JS/CSS              plain
React + Vite                   react
React + Vite (Typescript)      react-ts
Svelte + Vite                  svelte
Vanilla + Vite                 vanilla
Vue + Vite                     vue
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;for security reasons it is simplest to avoid 3rd party templates unless you've vetted them thoroughly&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I'll use "React + Vite (Typescript)": &lt;code&gt;wails init -n myapp -t react-ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Run the interactive developer view of the Application (it's responsive to rebuilding for changes): &lt;code&gt;wails dev&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Executing:&lt;span class="w"&gt; &lt;/span&gt;go&lt;span class="w"&gt; &lt;/span&gt;mod&lt;span class="w"&gt; &lt;/span&gt;tidy
&lt;span class="w"&gt;  &lt;/span&gt;•&lt;span class="w"&gt; &lt;/span&gt;Generating&lt;span class="w"&gt; &lt;/span&gt;bindings:&lt;span class="w"&gt; &lt;/span&gt;Done.
&lt;span class="w"&gt;  &lt;/span&gt;•&lt;span class="w"&gt; &lt;/span&gt;Installing&lt;span class="w"&gt; &lt;/span&gt;frontend&lt;span class="w"&gt; &lt;/span&gt;dependencies:&lt;span class="w"&gt; &lt;/span&gt;Done.
&lt;span class="w"&gt;  &lt;/span&gt;•&lt;span class="w"&gt; &lt;/span&gt;Compiling&lt;span class="w"&gt; &lt;/span&gt;frontend:&lt;span class="w"&gt; &lt;/span&gt;Done.
&lt;span class="w"&gt;  &lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In about 10 seconds you have your example native desktop app running locally.&lt;/p&gt;
&lt;h2 id="privacy-checks-in-the-configuration-files"&gt;Privacy checks in the configuration files&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;wails.json&lt;/code&gt; contains the application name - and your &lt;strong&gt;email address&lt;/strong&gt;, so do not send that to a git public repo unless you're ready for it&lt;/p&gt;
&lt;p&gt;Also, the name "myapp" will show up in a bunch of places so if you change/update the name, look for&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;grep&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'chat-explorer'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="w"&gt;    &lt;/span&gt;./go.mod
&lt;span class="w"&gt;    &lt;/span&gt;./wails.json
&lt;span class="w"&gt;    &lt;/span&gt;./frontend/index.html
&lt;span class="w"&gt;    &lt;/span&gt;./app.go
&lt;span class="w"&gt;    &lt;/span&gt;/main.go
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr/&gt;
&lt;h1 id="explaining-the-file-structure"&gt;Explaining the File Structure&lt;/h1&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;myapp/
├── build/
│   ├── appicon.png
│   ├── darwin/
│   │   ├── Info.plist
│   │   └── Info.dev.plist
│   └── windows/
├── frontend/
│   ├── src/
│   │   ├── App.tsx
│   │   ├── main.tsx
│   │   ├── App.css
│   │   └── style.css
│   ├── wailsjs/
│   │   ├── go/main/App.ts    &amp;lt;- auto-generated bindings
│   │   └── runtime/
│   ├── index.html
│   ├── package.json
│   └── tsconfig.json
├── app.go                    &amp;lt;- your Go application logic
├── main.go                   &amp;lt;- entrypoint
├── go.mod
├── go.sum
└── wails.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are two halves to understand: the Go backend and the React frontend.&lt;/p&gt;
&lt;h2 id="the-golang-backend"&gt;The Golang Backend&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;main.go&lt;/strong&gt; is the entrypoint for starting the application&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"embed"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"github.com/wailsapp/wails/v2"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"github.com/wailsapp/wails/v2/pkg/options"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"github.com/wailsapp/wails/v2/pkg/options/assetserver"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;//go:embed all:frontend/dist&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;assets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FS&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;NewApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;"myapp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;Width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;Height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;AssetServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;assetserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;Assets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;BackgroundColour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RGBA&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;G&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;OnStartup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;//go:embed all:frontend/dist&lt;/code&gt; is a directive doing something clever: embedding the entire compiled frontend into the Go binary at build time, no separate files to distribute!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Bind: []interface{}{ app,&lt;/code&gt; is the connection between "Go Exported Methods" and the Javascript/React&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;app.go&lt;/strong&gt; is the place for application logic (usually just high level, using /models or other places to get all of the details)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"context"&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;NewApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;startup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Hello "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;", welcome to Wails!"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The magic: every exported method on a bound struct (in this case "App") automatically gets a TypeScript binding generated in frontend/wailsjs/go/main/App.ts&lt;/p&gt;
&lt;h2 id="the-react-frontend"&gt;The React Frontend&lt;/h2&gt;
&lt;p&gt;Your usual &lt;strong&gt;frontend/index.html&lt;/strong&gt; has &lt;code&gt;&amp;lt;script src="./src/main.tsx" type="module"&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;frontend/src/main.tsx&lt;/strong&gt; is the entrypoint for starting the application - nothing surprising here:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'react'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'react-dom/client'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'./style.css'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'./App'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StrictMode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/React.StrictMode&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;frontend/src/App.tsx&lt;/strong&gt; is the bridge to the backend Golang Greet function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'react'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Greet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'../wailsjs/go/main/App'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setResult&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;greet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Greet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/button&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/p&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that the Greet function is a Promise, the call to the backend is async&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now explore adding your own function that returns a map like &lt;code&gt;func (a *App) GetCurrentTime() map[string]string {&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In the frontend you'd use it with&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GetCurrentTime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'../wailsjs/go/main/App'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// later in a component...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setInfo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;GetCurrentTime&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id="build-and-distribute"&gt;Build and Distribute&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;wails build&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;There are more instructions you'd need to follow about all the details of Windows, MacOS, etc.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;These are well known concepts of frontend (react) and backend (golang) so you can focus on your domain problems and features.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Using these standard technologies also makes it very easy to leverage AI/LLMs to write the code&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Stable building blocks and not re-inventing the wheel allows you to ship faster (and deliver value!)&lt;/p&gt;</content><category term="programming"/><category term="go"/><category term="golang"/><category term="react"/><category term="javascript"/><category term="js"/><category term="typescript"/><category term="desktop"/></entry><entry><title>Cars not helicopters, or running a local LLM with MLX on a Macbook Pro</title><link href="https://blog.john-pfeiffer.com/cars-not-helicopters-or-running-a-local-llm-with-mlx-on-a-macbook-pro/" rel="alternate"/><published>2024-11-23T19:21:00-08:00</published><updated>2024-11-23T19:21:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2024-11-23:/cars-not-helicopters-or-running-a-local-llm-with-mlx-on-a-macbook-pro/</id><summary type="html">
&lt;h1 id="everything-looks-like-a-nail"&gt;Everything looks like a nail&lt;/h1&gt;
&lt;p&gt;For Large Language Models (LLMs), Frontier aka "State of the Art" (SOTA) Models provided by big vendors like OpenAI and Anthropic and Google continue to add capabilities at a wondrous rate. Yet there's a very strong case for running LLMs locally, much like every smart …&lt;/p&gt;</summary><content type="html">
&lt;h1 id="everything-looks-like-a-nail"&gt;Everything looks like a nail&lt;/h1&gt;
&lt;p&gt;For Large Language Models (LLMs), Frontier aka "State of the Art" (SOTA) Models provided by big vendors like OpenAI and Anthropic and Google continue to add capabilities at a wondrous rate. Yet there's a very strong case for running LLMs locally, much like every smart phone provides a powerful computer your pocket.&lt;/p&gt;
&lt;p&gt;As an example: helicopters can cover in 15 minutes what takes 2 hours by car, but the overhead and extra considerations (fuel, pilots, landings, weather sensitivity, etc.) makes them impractical for routine tasks like picking up kids from school.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Though those new autonomous drones can carry pretty heavy loads ;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Given English has ~170,000 words but the average working vocabulary is 30,000 words, and  specific domains like "email" (Hello! Best Regards,) are even more narrow and formulaic...
Do you need a model trained on the entirety of human knowledge with latency from expensive GPU clusters?&lt;/p&gt;
&lt;p&gt;Writing code is specialized due to its utilitarian nature; it has even more structure, repetition, and rules - especially if it's type-checked and compiled.&lt;/p&gt;
&lt;h1 id="how-they-work-together"&gt;How they work together&lt;/h1&gt;
&lt;p&gt;In software, experienced engineers propose and design the architecture, whereas less experienced people do the (simpler parts) of implementation - specialization based on skill and scope.&lt;/p&gt;
&lt;p&gt;Frontier models can design and orchestrate, handle the unusual, while local models do specific, simple things well.&lt;/p&gt;
&lt;p&gt;Software engineers have learned to have a "plan" or "write a spec" phase specifically created with an advanced LLM, and that a local LLM can write out the code and tests for small well defined components.&lt;/p&gt;
&lt;h2 id="inevitable-and-resilient"&gt;Inevitable and Resilient&lt;/h2&gt;
&lt;p&gt;In 2020 an LLM was a research project and by 2024 it's running on your laptop (or smartphone!). Hardware gets better and costs go down which induces "Jevons Paradox" &lt;a href="https://en.wikipedia.org/wiki/Jevons_paradox"&gt;https://en.wikipedia.org/wiki/Jevons_paradox&lt;/a&gt; ; as people adapt they'll use LLM inference non-stop.&lt;/p&gt;
&lt;p&gt;Moreover, users (and businesses!) dependent to always having AI will balk at "cloud outages" or "AI outages" - thus a real need and demand for local LLMs.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Have you ever seen people struggling to function without mobile phone signal/reception ;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And I didn't even bring up the privacy and security arguments...&lt;/p&gt;
&lt;p&gt;We don't use Helicoptors for everything; use the right tool for the job.&lt;/p&gt;
&lt;h1 id="hands-on-with-mlx-install"&gt;Hands On with MLX - install&lt;/h1&gt;
&lt;p&gt;Apple's silicon architecture of "unified memory" is convenient for those trying out "Local LLMs" - and not buying a separate dedicated server with a GPU.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.apple.com/newsroom/2023/10/apple-unveils-m3-m3-pro-and-m3-max-the-most-advanced-chips-for-a-personal-computer/"&gt;https://www.apple.com/newsroom/2023/10/apple-unveils-m3-m3-pro-and-m3-max-the-most-advanced-chips-for-a-personal-computer/&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;uv
uv&lt;span class="w"&gt; &lt;/span&gt;--version
mkdir&lt;span class="w"&gt; &lt;/span&gt;llm-demo
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;llm-demo

uv&lt;span class="w"&gt; &lt;/span&gt;venv

&lt;span class="c1"&gt;## this installs mlx as it's a dependency of mlx-lm&lt;/span&gt;
uv&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;mlx-lm

&lt;span class="c1"&gt;## optional sanity checks - using explicit calls with the local uv&lt;/span&gt;
uv&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;list
uv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"import mlx; import mlx.core as mx; print('mlx ok', mx.__version__)"&lt;/span&gt;
uv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"import mlx_lm; print('mlx_lm ok')"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="run-that-llm-with-mlx"&gt;Run that LLM with MLX&lt;/h2&gt;
&lt;p&gt;The following command will both download the model and then load it into memory along with sending it the prompt:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;uv run mlx_lm.generate --model mlx-community/Meta-Llama-3.1-8B-Instruct-4bit --prompt "tell me a joke"&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A man walked into a library and asked the librarian, "Do you have any books on Pavlov's dogs and Schrödinger's cat?" The librarian replied, "It rings a bell, but I'm not sure if it's here or not."&lt;/p&gt;
&lt;p&gt;Prompt: 39 tokens, 75.536 tokens-per-sec
  Generation: 54 tokens, 31.247 tokens-per-sec
  Peak memory: 4.638 GB&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For LLMs a "token" is a part of word - and this output rate of generating tokens is plenty fast enough to not wait while each word of the joke is printed slowly.&lt;/p&gt;
&lt;p&gt;Under the hood, let's examine where the "open weight" downloaded model is:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;du -sh ~/.cache/huggingface/hub/models--mlx-community--Meta-Llama-3.1-8B-Instruct-4bit&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;4.2G  models--mlx-community--Meta-Llama-3.1-8B-Instruct-4bit&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="next-level-is-a-python-script"&gt;Next Level is a Python Script&lt;/h1&gt;
&lt;p&gt;Create the wrapper script "llm-demo/mychat.py"&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mlx_lm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Usage: python mychat.py 'your prompt here'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mlx-community/Meta-Llama-3.1-8B-Instruct-4bit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;assuming you are re-using the "llm-demo" directory and all the pre-requisite UV and venv setup&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To create the pyproject.toml and ensure the mlx-lm dependency is added run the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;uv init --bare
cat pyproject.toml
uv add mlx-lm
cat pyproject.toml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now leveraging the python environment to "just run python" rather than calling UV for everything...&lt;/p&gt;
&lt;p&gt;&lt;code&gt;source .venv/bin/activate&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;python mychat.py "tell me a joke"&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You may notice it ran on telling multiple jokes and also abruptly terminated at the end...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="improving-the-output-with-a-formatted-prompt"&gt;Improving the output with a formatted prompt&lt;/h2&gt;
&lt;p&gt;The following code changes formats the prompt the way the instruction-tuned model expects, returning a more natural&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mlx_lm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Usage: python mychat.py 'your prompt here'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mlx-community/Meta-Llama-3.1-8B-Instruct-4bit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;

&lt;span class="c1"&gt;# Provide the role and chat template format&lt;/span&gt;
&lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply_chat_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;add_generation_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;A much better joke =)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/ml-explore/mlx-lm/blob/main/mlx_lm/generate.py"&gt;https://github.com/ml-explore/mlx-lm/blob/main/mlx_lm/generate.py&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="ram-usage-during-inference"&gt;RAM usage during Inference&lt;/h2&gt;
&lt;p&gt;When the model is loaded into memory, the most dynamic part of what changes is the "context" - everything provided in the Prompt, and the output.&lt;/p&gt;
&lt;p&gt;When there's too much context going in or output coming then the Local LLM can consume all of the available RAM.&lt;/p&gt;
&lt;p&gt;Open "activity monitor" and choose "Memory"&lt;/p&gt;
&lt;p&gt;Run the following to force more context into the "KV cache" and observe the "Python" application memory slowly creep upward&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;uv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;mlx_lm.generate&lt;span class="w"&gt; &lt;/span&gt;--model&lt;span class="w"&gt; &lt;/span&gt;mlx-community/Meta-Llama-3.1-8B-Instruct-4bit&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--prompt&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write a detailed 5000-word essay on the history of computing"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--max-tokens&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="simulation-of-memory-in-a-conversation"&gt;Simulation of memory in a conversation&lt;/h2&gt;
&lt;p&gt;Each call to the LLM is stateless, so a wrapper application keeps track of every back and forth interaction during a "conversation".&lt;/p&gt;
&lt;p&gt;This allows the LLM to reference things "earlier" in the conversation - all of which is passed in as context for the new Prompt.&lt;/p&gt;
&lt;p&gt;Ergo longer conversations will take up more RAM.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mlx_lm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mlx-community/Meta-Llama-3.1-8B-Instruct-4bit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;system_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"You are a helpful, concise Assistant"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;system_message&lt;/span&gt;&lt;span class="p"&gt;},]&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"MLX Chat. Type 'exit' or Ctrl+C to quit.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;user_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"You: "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"exit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"quit"&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply_chat_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;add_generation_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Assistant: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Exiting."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="openai-compatible-http-server"&gt;OpenAI compatible HTTP Server&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;uv run mlx_lm.server --model mlx-community/Meta-Llama-3.1-8B-Instruct-4bit --host 127.0.0.1 --port 8080&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl 127.0.0.1:8080/v1/models&lt;/code&gt;&lt;/p&gt;
&lt;h1 id="troubleshooting"&gt;Troubleshooting&lt;/h1&gt;
&lt;h2 id="where-is-that-installed"&gt;Where is that installed?&lt;/h2&gt;
&lt;p&gt;A python uv gotcha - do not move (or copy) the .venv directory since it includes absolute folder paths - instead use the uv commands for each new project.&lt;/p&gt;
&lt;p&gt;UV is awesomely fast - one reason is it uses a cache, here's how to audit how many python versions UV installed/knows:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ls -ahl ~/.local/share/uv/python/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And in case you installed uv with both "install.sh" and homebrew...&lt;/p&gt;
&lt;p&gt;&lt;code&gt;which -a uv&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/opt/homebrew/bin/uv
~/.local/bin/uv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="cleanup"&gt;Cleanup&lt;/h2&gt;
&lt;p&gt;To find previously downloaded locl models, which are large files&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="w"&gt; &lt;/span&gt;-type&lt;span class="w"&gt; &lt;/span&gt;f&lt;span class="w"&gt; &lt;/span&gt;-size&lt;span class="w"&gt; &lt;/span&gt;+1G&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&amp;gt;/dev/null
ls&lt;span class="w"&gt; &lt;/span&gt;-ahl&lt;span class="w"&gt; &lt;/span&gt;~/.cache/huggingface/
ls&lt;span class="w"&gt; &lt;/span&gt;-ahl&lt;span class="w"&gt; &lt;/span&gt;~/.cache/huggingface/hub
rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;~/.cache/huggingface/hub/models--mlx-community--Meta-Llama-3...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or maybe you want to clean up a global pip install of MLX&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip3&lt;span class="w"&gt; &lt;/span&gt;list
brew&lt;span class="w"&gt; &lt;/span&gt;uninstall&lt;span class="w"&gt; &lt;/span&gt;mlx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id="what-next"&gt;What Next?&lt;/h1&gt;
&lt;p&gt;The right tool for the job: Consider how you leverage and architect this new technology.&lt;/p&gt;
&lt;p&gt;Cars may not be as exciting as vertical take off and landing - but maybe solutions don't always have to be exciting.&lt;/p&gt;
&lt;p&gt;There is a lot of value in a sub 10ms answer that's practically free.&lt;/p&gt;
&lt;h1 id="references"&gt;References&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://github.com/ml-explore/mlx"&gt;https://github.com/ml-explore/mlx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Upcoming post&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Llama 3.1 is a good general model, but it was not created to focus on coding
&lt;code&gt;uv run mlx_lm.generate  --model mlx-community/Qwen2.5.1-Coder-7B-Instruct-8bit --prompt ""&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Addendum:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://machinelearning.apple.com/research/exploring-llms-mlx-m5"&gt;https://machinelearning.apple.com/research/exploring-llms-mlx-m5&lt;/a&gt;&lt;/p&gt;</content><category term="ai"/><category term="ai"/><category term="llm"/><category term="mlx"/></entry><entry><title>React with Material-UI and Google SSO</title><link href="https://blog.john-pfeiffer.com/react-with-material-ui-and-google-sso/" rel="alternate"/><published>2024-09-07T12:34:00-07:00</published><updated>2024-09-07T12:34:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2024-09-07:/react-with-material-ui-and-google-sso/</id><summary type="html">
&lt;p&gt;tldr: React + Vite + MUI + simple Google sign-in for rapid building&lt;/p&gt;
&lt;p&gt;React is almost a defacto standard for responsive frontend applications, so it can be helpful to layer on a few more "standard choices" to simplify building apps for production.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;if you need a more basic react intro see my previous …&lt;/em&gt;&lt;/p&gt;</summary><content type="html">
&lt;p&gt;tldr: React + Vite + MUI + simple Google sign-in for rapid building&lt;/p&gt;
&lt;p&gt;React is almost a defacto standard for responsive frontend applications, so it can be helpful to layer on a few more "standard choices" to simplify building apps for production.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;if you need a more basic react intro see my previous post &lt;a href="https://blog.john-pfeiffer.com/react-javascript-intro/"&gt;https://blog.john-pfeiffer.com/react-javascript-intro/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h1 id="history-of-style"&gt;History of Style&lt;/h1&gt;
&lt;p&gt;As HTML took over the world, the need became obvious for separating out the "styling" of the presentation and content. Thus CSS was created, though it took awhile to become a fully adopted and usable standard (due to interests that would have preferred a private single browser definition and control).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/CSS"&gt;https://en.wikipedia.org/wiki/CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ascii diagram:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────┐
│  SOURCE CODE FILES (what you write)                         │
│                                                             │
│   index.html          style.css                             │
│   ┌───────────┐       ┌──────────────────┐                  │
│   │ &amp;lt;h1&amp;gt;      │       │ h1 {             │                  │
│   │ &amp;lt;p&amp;gt;       │       │   color: blue;   │                  │
│   │ &amp;lt;button&amp;gt;  │       │ }                │                  │
│   └───────────┘       └──────────────────┘                  │
│         │                     │                             │
│         └──────────┬──────────┘                             │
│                    ▼                                        │
│              ┌───────────┐                                  │
│              │  Browser  │                                  │
│              └─────┬─────┘                                  │
│                    ▼                                        │
│   ┌─────────────────────────────────────────────────────┐   │
│   │  DOM (Document Object Model) - what the browser     │   │
│   │  builds in memory, combining structure + style      │   │
│   │                                                     │   │
│   │  document                                           │   │
│   │    └── html                                         │   │
│   │          └── body                                   │   │
│   │                ├── h1 (color: blue)                 │   │
│   │                ├── p                                │   │
│   │                └── button                           │   │
│   └─────────────────────────────────────────────────────┘   │
│                    │                                        │
│                    ▼                                        │
│            What you see on screen                           │
└─────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As an application grows, or if you are not keen on customizing every detail of the style, it can become overwhelming managing a lot of CSS files (and which setting overrides which thing).&lt;/p&gt;
&lt;h2 id="material-ui-for-styling"&gt;Material UI for Styling&lt;/h2&gt;
&lt;p&gt;Material UI (MUI) provides pre-built components that look decent out of the box and handle responsive design, accessibility, and theming.&lt;/p&gt;
&lt;p&gt;It is a framework that generates both HTML and CSS for you - so it is simple, but opinionated.&lt;/p&gt;
&lt;p&gt;To add Material UI dependencies to an existing React project:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;npm&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;@mui/material&lt;span class="w"&gt; &lt;/span&gt;@emotion/react&lt;span class="w"&gt; &lt;/span&gt;@emotion/styled
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://mui.com/material-ui/getting-started/"&gt;https://mui.com/material-ui/getting-started/&lt;/a&gt;&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;example package.json&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"material-ui-example"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"preview"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite preview"&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"@emotion/react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^11.14.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"@emotion/styled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^11.14.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"@mui/material"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.3.6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"react-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"react-router-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.20.0"&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"@types/react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"@types/react-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"@vitejs/plugin-react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"vite"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.1.3"&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/details&gt;
&lt;h3 id="a-simple-react-and-mui-code-example"&gt;A simple React and MUI code example&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;App.jsx&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'react'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Typography&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'@mui/material'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hello world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"more content on line 2"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"github.com/mui has more info"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;activeContentIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setActiveContentIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mb&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setActiveContentIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/Button&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setActiveContentIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/Button&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/Box&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;activeContentIndex&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Typography&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;paragraph&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/Typography&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;))}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/Box&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Box is a Material-UI generic wrapper: a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; that accepts the sx property&lt;/li&gt;
&lt;li&gt;sx is a Material-UI shorthand for a property&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;p = padding, all sides, 2 x 8px = 16px&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;m = margin-bottom, 2 x 8px = 16px&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Button has Material Design ripple effect, hover states, and theming&lt;/li&gt;
&lt;li&gt;Typography applies Material Design font family, sizes, and spacing and renders as &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; by default&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;There is no longer &lt;code&gt;App.css&lt;/code&gt; and &lt;code&gt;index.css&lt;/code&gt; since MUI is handling all the styling.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Thus your React framework code is now React + Material-UI, effectively a "Domain Specific Language" that quickly builds and renders frontend apps.&lt;/p&gt;
&lt;hr/&gt;
&lt;h1 id="google-single-sign-on"&gt;Google Single Sign On&lt;/h1&gt;
&lt;p&gt;There are quite a few options for security, and identity (followed by authentication and authorization), and if you attempt to write it all yourself you can inadvertantly create a security issue.&lt;/p&gt;
&lt;p&gt;One of the most popular Identity Providers to leverage is Google (due to gmail and youtube and large consumer reach).&lt;/p&gt;
&lt;p&gt;For simple apps where your users will already have a Google Account, here is an easy way to have them verify their identity with Google and then share their email address with your application.&lt;/p&gt;
&lt;p&gt;This allows for a "frontend only" app to have personalization, and is an important pre-requisite when added to a backend.&lt;/p&gt;
&lt;h2 id="create-a-google-oauth-client"&gt;Create a Google OAuth Client&lt;/h2&gt;
&lt;p&gt;For an existing (or new) &lt;strong&gt;Google Cloud Project&lt;/strong&gt;, start the flow with the OAuth Consent&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Requests user consent so your app can access the user's data&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Create https://console.cloud.google.com/auth/overview/create&lt;/li&gt;
&lt;li&gt;fill out the questionnaire about the Application and intended use (starting with Internal or Test users is simplest)&lt;/li&gt;
&lt;li&gt;fill in contact email address, and "terms and conditions"&lt;/li&gt;
&lt;li&gt;"OAuth configuration created"&lt;/li&gt;
&lt;li&gt;Create  OAuth client (button)&lt;/li&gt;
&lt;li&gt;Application type: web application&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;http://localhost:5173&lt;/code&gt; to Authorized JavaScript origins (for local dev)&lt;/li&gt;
&lt;li&gt;if you know it already you can add the domain where your production React App runs (i.e. https://yourusername.github.io/)&lt;/li&gt;
&lt;li&gt;Create (button)&lt;/li&gt;
&lt;li&gt;Copy the Client ID - this is what you put in your environment variables or code&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;i.e. really-long-random-numbers-and-characters.apps.googleusercontent.com&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note that this client ID is a public facing configuration value (ends up in JS code visible in the browser dev tools)&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="environment-variable"&gt;Environment Variable&lt;/h2&gt;
&lt;p&gt;Using an Environment variable allows setting a different value for Development, Staging, and Production.&lt;/p&gt;
&lt;p&gt;Create the file &lt;code&gt;.env&lt;/code&gt; in your project root with the following in it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;VITE_GOOGLE_CLIENT_ID=your-client-id-here.apps.googleusercontent.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;(Also make sure you add &lt;code&gt;.env&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; ;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Make sure you also set the value in your build process (or via integration with a hosting provider like AWS Amplify or Cloudflare ) that creates the final bundle &lt;/p&gt;
&lt;h3 id="load-the-google-identity-services-script"&gt;Load the Google Identity Services Script&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;index.html&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;My SSO App&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://accounts.google.com/gsi/client"&lt;/span&gt; &lt;span class="na"&gt;async&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;noscript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;You need to enable JavaScript to run this app.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;noscript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/src/main.jsx"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="google-sign-in-component"&gt;Google Sign-In Component&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;GoogleSignIn.jsx&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GoogleSignIn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;onAuth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;gsiReady&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setGsiReady&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;authStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setAuthStatus&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;googleButtonRef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;renderedRef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clientId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_GOOGLE_CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;setAuthStatus&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Missing VITE_GOOGLE_CLIENT_ID"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;handleCredentialResponse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;setAuthStatus&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onAuth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;onAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tryRender&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;googleButtonRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tryRender&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;setGsiReady&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Prevent double-render (React 18 StrictMode calls useEffect twice)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderedRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;renderedRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;handleCredentialResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;googleButtonRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;renderButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;googleButtonRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"outline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"large"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"signin_with"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;tryRender&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;onAuth&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;googleButtonRef&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;gsiReady&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Google&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Sign&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;In&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;authStatus&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'red'&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;authStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="using-the-sign-in-component"&gt;Using the Sign-In Component&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;App.jsx&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'react'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Typography&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'@mui/material'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GoogleSignIn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'./GoogleSignIn'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;handleAuth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// The credential is a JWT - decode the payload to get user info&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Container&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;maxWidth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sm"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Paper&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;elevation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Typography&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"h6"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Welcome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/Typography&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Typography&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"text.secondary"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/Typography&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GoogleSignIn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;onAuth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleAuth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/Paper&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/Container&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The JWT payload from Google includes: &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;picture&lt;/code&gt;, &lt;code&gt;sub&lt;/code&gt; (unique user ID), and expiration info.&lt;/p&gt;
&lt;hr/&gt;
&lt;h1 id="listing-all-the-files-and-a-diagram"&gt;Listing all the files and a diagram&lt;/h1&gt;
&lt;p&gt;Final folder structure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;VITE_GOOGLE_CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gitignore&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;gsi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jsx&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jsx&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;GoogleSignIn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jsx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;For deployment, remember to add your production domain to the Google OAuth credentials Authorized JavaScript origins.&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────────┐
│  GOOGLE SSO (Client-Side Only)                                      │
│                                                                     │
│                                                                     │
│   ┌──────────┐                              ┌──────────────────┐    │
│   │  Browser │                              │  Google Servers  │    │
│   └────┬─────┘                              └────────┬─────────┘    │
│        │                                             │              │
│        │  1. Page loads GSI script                   │              │
│        │────────────────────────────────────────────&amp;gt;│              │
│        │&amp;lt;────────────────────────────────────────────│              │
│        │                                             │              │
│        │  2. User clicks "Sign in with Google"       │              │
│        │────────────────────────────────────────────&amp;gt;│              │
│        │                                             │              │
│        │         ┌─────────────────────┐             │              │
│        │         │  Google login popup │             │              │
│        │         │  - enter email      │             │              │
│        │         │  - enter password   │             │              │
│        │         │  - consent screen   │             │              │
│        │         └─────────────────────┘             │              │
│        │                                             │              │
│        │  3. Google returns JWT credential           │              │
│        │&amp;lt;────────────────────────────────────────────│              │
│        │                                             │              │
│        │                                                            │
│        ▼                                                            │
│   ┌─────────────────────────────────────────┐                       │
│   │  JWT Payload (decoded in browser by javascript)                 │
│   │                                         │                       │
│   │  {                                      │                       │
│   │    "sub": "1234567890",  ← unique ID    │                       │
│   │    "email": "user@gmail.com",           │                       │
│   │    "name": "John Smith",                │                       │
│   │    "picture": "https://...",            │                       │
│   │    "exp": 1234567890                    │                       │
│   │  }                                      │                       │
│   └─────────────────────────────────────────┘                       │
│                                                                     │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="important-caveats"&gt;Important Caveats&lt;/h2&gt;
&lt;p&gt;This all depends on trusting the javascript provided by google and their servers - they are responsible for the complexities of passwords, 2-factor-authentication, etc. &lt;/p&gt;
&lt;p&gt;This post uses Google Identity Services to get an ID token directly in the browser (no redirect URI required).&lt;/p&gt;
&lt;p&gt;If you use "OAuth authorization code flow", you must configure redirect URIs and handle the code exchange on a backend.&lt;/p&gt;
&lt;p&gt;Importantly: if you have any backend/API you should send the Google ID token to the backend and have code to truly verify it (signature + issuer + audience + expiry).&lt;/p&gt;
&lt;h2 id="bonus-backend-golang-code"&gt;Bonus Backend Golang Code&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"context"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"log"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"net/http"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"google.golang.org/api/idtoken"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;googleAuthReq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;IDToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:"id_token"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;handleGoogleAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;googleAuthReq&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Invalid request"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusBadRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;clientID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"your-client-id-here.apps.googleusercontent.com"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;idtoken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IDToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clientID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Invalid ID token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusUnauthorized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// payload.Claims contains "email", "email_verified", "name", etc.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Claims&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;].(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;emailVerified&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Claims&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"email_verified"&lt;/span&gt;&lt;span class="p"&gt;].(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;emailVerified&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Invalid email or email not verified"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusUnauthorized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Claims&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;mux&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewServeMux&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;mux&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/{path...}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;handleGoogleAuth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mux&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to start server: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;curl -X POST http://localhost:8080/ -H "Content-Type: application/json" --data '{"id_token":"foobar"}'&lt;/code&gt;&lt;/p&gt;</content><category term="programming"/><category term="react"/><category term="javascript"/><category term="js"/><category term="material-ui"/><category term="sso"/><category term="oauth"/></entry><entry><title>React Javascript Intro</title><link href="https://blog.john-pfeiffer.com/react-javascript-intro/" rel="alternate"/><published>2024-08-25T12:34:00-07:00</published><updated>2024-08-25T12:34:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2024-08-25:/react-javascript-intro/</id><summary type="html">
&lt;p&gt;The intent of this post is to help people build and understand simple reactive web applications&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2025 update&lt;/strong&gt;: due to the never-ending nature of tech breaking things - this post needed an update (goodbye Create React App)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://react.dev/blog/2025/02/14/sunsetting-create-react-app"&gt;https://react.dev/blog/2025/02/14/sunsetting-create-react-app&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="pre-requisites"&gt;Pre-Requisites&lt;/h1&gt;
&lt;p&gt;install node.js (which also …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;The intent of this post is to help people build and understand simple reactive web applications&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2025 update&lt;/strong&gt;: due to the never-ending nature of tech breaking things - this post needed an update (goodbye Create React App)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://react.dev/blog/2025/02/14/sunsetting-create-react-app"&gt;https://react.dev/blog/2025/02/14/sunsetting-create-react-app&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="pre-requisites"&gt;Pre-Requisites&lt;/h1&gt;
&lt;p&gt;install node.js (which also includes &lt;code&gt;npm&lt;/code&gt;) from &lt;a href="https://nodejs.org/en"&gt;https://nodejs.org/en&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;check if they are installed:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;node -v&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm -v&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="install-the-default-react-project"&gt;Install the default react project&lt;/h2&gt;
&lt;p&gt;in a directory (probably a code repository)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;npm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vite&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;react&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;npm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;npm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;(obsolete: npx create-react-app my-app)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;note that &lt;code&gt;npm run dev&lt;/code&gt; starts a web server at http://localhost:5173&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Essential folder structure (that is created by the framework with default files)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;my-app/&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;index&lt;/span&gt;.&lt;span class="nv"&gt;html&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;entrypoint&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;yes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="err"&gt;'s all just html and http ;)&lt;/span&gt;

&lt;span class="err"&gt;    src/&lt;/span&gt;
&lt;span class="err"&gt;        index.css      (this is for styles for the output - if you want things to look good)&lt;/span&gt;
&lt;span class="err"&gt;        main.jsx       (connects the App to the starting index.html entrypoint)&lt;/span&gt;
&lt;span class="err"&gt;        App.css&lt;/span&gt;
&lt;span class="err"&gt;        App.jsx     (this is where you add code)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;/public&lt;/strong&gt; is a directory with image or binary files that are served directly&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Remove the "vitals" phone home stats because privacy should be a default)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;INDEX.HTML&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The defacto web standard for the first document (for a web server) to return when no resource is specified is "index.html"&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/HTML"&gt;https://en.wikipedia.org/wiki/HTML&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    &lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;noscript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;You need to enable JavaScript to run this app.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;noscript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/src/main.jsx"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;MAIN.JSX&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// this wrapper is connecting the App to the root&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'react'&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'react-dom/client'&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'./index.css'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'./App.jsx'&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rootElement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StrictMode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/React.StrictMode&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;APP.JSX&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// very simple app that switches the content displayed when the button is clicked&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'./App.css'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'react'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hello world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"more content on line 2"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"react.dev has tutorials and documentation"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;activeContentIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;activeContent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"App"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"App-header"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/header&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tabs"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;activeContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Tab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/button&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;activeContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Tab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/button&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tab-content"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;activeContentIndex&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/p&amp;gt;)}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;A React component is a JavaScript function that returns JSX (looks like HTML but is not HTML)&lt;/em&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h1 id="build-and-deploy"&gt;Build and Deploy&lt;/h1&gt;
&lt;p&gt;A critical file created by the default app generation is &lt;code&gt;package.json&lt;/code&gt;, it lists all your project dependencies and scripts (including "how to build")&lt;/p&gt;
&lt;p&gt;The build framework reads your package.json to understand which tools to run, in this case &lt;code&gt;vite&lt;/code&gt; compiles/reformats the code into a production bundle of files that can be uploaded to a server&lt;/p&gt;
&lt;p&gt;The command to "build", or convert all of these files into a single bundle that is easily deployed:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The new output directory &lt;code&gt;dist&lt;/code&gt; contains the index.html and static files along with "minified" javascript and css&lt;/p&gt;
&lt;p&gt;&lt;a href="https://vite.dev/guide/static-deploy.html"&gt;https://vite.dev/guide/static-deploy.html&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="react"/><category term="javascript"/><category term="js"/></entry><entry><title>Behind the scenes of the challenges for a Hiring Manager</title><link href="https://blog.john-pfeiffer.com/behind-the-scenes-of-the-challenges-for-a-hiring-manager/" rel="alternate"/><published>2024-04-29T12:01:00-07:00</published><updated>2024-04-29T12:01:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2024-04-29:/behind-the-scenes-of-the-challenges-for-a-hiring-manager/</id><summary type="html">
&lt;h1 id="hiring-is-more-than-just-a-single-decision"&gt;Hiring is more than just a single decision&lt;/h1&gt;
&lt;p&gt;A lot of interviewing advice is for candidates. I wanted to pull back the curtain on some experiences and tips for hiring managers, especially if it's your first time and you just got a headcount. =)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Hopefully this is also useful if you're …&lt;/em&gt;&lt;/p&gt;</summary><content type="html">
&lt;h1 id="hiring-is-more-than-just-a-single-decision"&gt;Hiring is more than just a single decision&lt;/h1&gt;
&lt;p&gt;A lot of interviewing advice is for candidates. I wanted to pull back the curtain on some experiences and tips for hiring managers, especially if it's your first time and you just got a headcount. =)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Hopefully this is also useful if you're training people managers or ICs on the whole process)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Hiring exists to solve a business need. It can take as little as a few weeks, as long as a year or more. Like many management responsibilities, it is more than just a single decision, often it's creating an effective system.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Hiring Manager is always accountable for the decision to hire, the process of hiring, and the results of hiring (or not hiring)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="backstory-on-how-a-role-opens-up"&gt;Backstory on How a Role Opens Up&lt;/h2&gt;
&lt;p&gt;There is a lot more to the story for a straightforward job posting like "Senior Engineer".&lt;/p&gt;
&lt;p&gt;Here's a short list of reasons that a position may have opened up - and how that influences the hiring process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rapid growth of users/customers&lt;/li&gt;
&lt;li&gt;gaps (think reliability, bugs) due to growth&lt;/li&gt;
&lt;li&gt;very lean team reaching a breaking point&lt;/li&gt;
&lt;li&gt;someone left&lt;/li&gt;
&lt;li&gt;someone was asked to leave&lt;/li&gt;
&lt;li&gt;a completely new team (or even whole new initiative)&lt;/li&gt;
&lt;li&gt;org shuffle&lt;/li&gt;
&lt;li&gt;newly discovered gap in critical skills&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Growth headcount&lt;/strong&gt; is a calculated bet: the organization is investing in a direction.&lt;/p&gt;
&lt;p&gt;The approval can hinge on this being aligned with larger leadership strategy and initiatives, and who specifically has budget. Opportunity cost can be subjective, and if this hire comes at the expense of another team?&lt;/p&gt;
&lt;p&gt;People in the existing team may wonder why someone's getting hired "above them", or if it means more work and distraction doing interviewing and onboarding, or even worry about this being the beginning of them being replaced.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Backfills&lt;/strong&gt; are the most straightforward: the organization was paying for a certain amount of work to be done and now needs a replacement. While the approval can be straightforward due to previously allocated budget, it can also mean there's prior expectations.&lt;/p&gt;
&lt;p&gt;The existing team/colleagues may be concerned: either that the new person won't be able to do as much as their previous colleague, or that they'll have similar or worse flaws as their departed coworker.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The old saying is "Fool me once, shame on you, fool me twice, shame on the Hiring Manager"&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Either way, the team is carrying extra load until the new person starts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shuffles and Discoveries&lt;/strong&gt; are the most at risk. Sometimes the org or plan changed, and it could change again. These headcount may not map cleanly to a business narrative or stable org structure. Sometimes the approval was verbal and never really hardened into a durable commitment. Maybe leadership wants a new capability, but the business case is still fuzzy.&lt;/p&gt;
&lt;p&gt;People may ask why someone new is needed for a specific new skill or background/experience instead of uplevelling existing folks.&lt;/p&gt;
&lt;p&gt;Ultimately it will be asked:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If this role exists, what other role does not? &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="deciding-the-level"&gt;Deciding the Level&lt;/h2&gt;
&lt;p&gt;Here's where the challenge gets harder. Level determines compensation band, which determines budget.&lt;/p&gt;
&lt;p&gt;Does your budget really support current market rates for that senior engineer you're sure you need?&lt;/p&gt;
&lt;p&gt;What's the distribution of seniority in your team(s), and even the wider org?&lt;/p&gt;
&lt;p&gt;Are people needing a mentor - or mentees?&lt;/p&gt;
&lt;p&gt;There can be pressure to down-level (which creates room for others in the team to step up) or up-level (which adds skills and bandwidth that may accelerate/improve the team).&lt;/p&gt;
&lt;p&gt;Then there's reality of who's applying: if you post for Principal Engineer it's a smaller pool of candidates - which can make finding the right one take a lot longer (or cost more).&lt;/p&gt;
&lt;p&gt;Also, candidates at higher levels (more scope, more comp) need to go through more vetting. That may mean navigating cross-org or "all-the-way-to-the-top" approvals.&lt;/p&gt;
&lt;p&gt;And candidates may shy away from the increased requirements and extended process. You may have to innovate new ways of assessing these rare candidates, and get approval for that new process too.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;One critical tip: if after a couple of months the candidate pipeline isn't quite matching - revisit the job requirements and level.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Something I've personally seen: opening a role at Senior for a wider pool, then having an amazing candidate who will only accept Staff, needing to go back to HR/Finance and renegotiate. In the extra time it took to renegotiate the level, the candidate accepted another offer.&lt;/p&gt;
&lt;h3 id="tools-of-the-trade"&gt;Tools of the Trade&lt;/h3&gt;
&lt;p&gt;Politics: the art of influencing others.&lt;/p&gt;
&lt;p&gt;So first get the facts and strive for first-principles thinking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What does the company truly need now and for the next 12 months?&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Get feedback from your manager, your manager's manager, and your peers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;What element needs to be added into the chemistry of the team?&lt;/li&gt;
&lt;li&gt;What can you truly afford - now and during the next compensation and promotion cycle?&lt;/li&gt;
&lt;li&gt;What's the opportunity cost for every month the role isn't filled?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Avoid hiring for vanity metrics ("doubled my team") or empire-building ("never dependent on other teams").&lt;/p&gt;
&lt;p&gt;The uncomfortable truth: sometimes headcount is assigned because of existing relationships or to keep someone  happy.&lt;/p&gt;
&lt;p&gt;Long term credibility comes from being seen as objective with decisions that lead to achieving the larger goals, from having a well documented track record of delivering results.&lt;/p&gt;
&lt;p&gt;Helping new managers with their hiring process has been some of the most rewarding and impactful work I've done as a people manager:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I helped a peer advocate for and successfully hire 2 great candidates when there was only 1 headcount available by looking at the company's own 12-month forecast&lt;/li&gt;
&lt;li&gt;I've pointed out some missing topics in interviewing loops which raised the quality of the candidate debriefs&lt;/li&gt;
&lt;li&gt;I've supported a Manager who struggled to say No to everyone on her team, when they kept saying "Yes" to every candidate; via private 1:1s everyone came to understand the real impact of a bad hire&lt;/li&gt;
&lt;li&gt;I've even transferred people to another team when they really needed it but didn't have headcount&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="writing-the-job-requisition"&gt;Writing the Job Requisition&lt;/h1&gt;
&lt;p&gt;The job description you see on a careers page has been through a journey to get there. It's actually a myriad of compromises about what the manager wants, the team needs, what the company can support and how the company presents itself externally.&lt;/p&gt;
&lt;p&gt;It starts with a hiring manager writing briefly what they actually need. This is the important part: documenting what would truly move the organization forward, not just a wish list.&lt;/p&gt;
&lt;p&gt;Then the manager of the hiring manager looks it over and considers the context of org priorities and patterns, historical trends for similar roles.&lt;/p&gt;
&lt;p&gt;HR reviews and adjusts it for legal/company policy adherence.&lt;/p&gt;
&lt;p&gt;Then recruiting edits it for marketing/SEO, company messaging, and consistency with existing job templates.&lt;/p&gt;
&lt;p&gt;Since the final polished posting can be different from the original Hiring Manager's version, experienced candidates know to ask, "What does success look like for this role in the first 6 months?"&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is smart because it begins the conversation of alignment on impact - everything on the "required" list may not be needed until month 9+&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="working-together-with-your-recruiter"&gt;Working Together with your Recruiter&lt;/h2&gt;
&lt;p&gt;Maybe you've been wondering the whole time: wait, the Recruiter's last?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If they are last and they just take the req and blindly post it then you may have a long and painful process of starting with poorly-matched candidates =(&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The recruiter is not your assistant&lt;/strong&gt;, they are a skilled professional in a specific domain to achieve company goals.&lt;/p&gt;
&lt;p&gt;The collaboration between hiring manager and recruiter is essential for success. Recruiting/Talent have Key Performance Indicators about the number of resumes and candidates screened, interview funnel conversion rates, offers vs accepted, and candidate engagement feedback.&lt;/p&gt;
&lt;p&gt;They are also almost always juggling multiple reqs and pipelines at once.&lt;/p&gt;
&lt;p&gt;A good recruiter asks questions like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For candidates, what's the narrative on why this role is open, why the team and project is great?&lt;/li&gt;
&lt;li&gt;What's your dealbreaker(s) for passing on a candidate?&lt;/li&gt;
&lt;li&gt;What's one requirement or skill you might compromise on? &lt;/li&gt;
&lt;li&gt;What are examples of existing employees that'd match the role?&lt;/li&gt;
&lt;li&gt;How diverse is your team, and how diverse is the interview panel?&lt;/li&gt;
&lt;li&gt;How long would you wait before down-levelling to widen the candidate pool?&lt;/li&gt;
&lt;li&gt;How willing are you to up-level or consider a signing bonus for a great candidate?&lt;/li&gt;
&lt;li&gt;How will the candidate perform and feel after 6 hours of back to back interviews?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They will tell you historically how long it usually takes to hire this role, if it's a "employer's market" or "candidate's market", and bluntly: that you might be looking for a flying unicorn.&lt;/p&gt;
&lt;h3 id="calibration-and-sourcing"&gt;Calibration and Sourcing&lt;/h3&gt;
&lt;p&gt;Early alignment at the top of the funnel saves a lot of time and effort: what profile is most similar to candidates who will do well during interviewing and get an offer.&lt;/p&gt;
&lt;p&gt;An exercise I've been through many times to great effect is having the Recruiter provide a list of randomly selected candidate profiles/resumes and scoring them to 1-5 for how "strong" they seem for the role.&lt;/p&gt;
&lt;p&gt;While the first round can be async, what's truly important is to have an actual meeting talking through the Why one candidate really seems better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sourcing&lt;/strong&gt; is finding the channels and methods to reach out to candidates. Your Recruiter/Talent team may know the currently best performing places. Managers and ICs may know the industry niche or geography-specific places that are really high signal.&lt;/p&gt;
&lt;p&gt;Finding those proxies for "Who will probably do well" and "Where can I reach them" are the foundation for your pipeline.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Referrals: traditionally the strongest positive signal as someone actually worked with and vouches for this person. These candidates will understandably get special treatment, and it requires diplomacy if things don't work out.&lt;/li&gt;
&lt;li&gt;Sourced: they didn't actively come to you, they were found because they fit your target profile. A personal message from a Hiring Manager starts a human connection.&lt;/li&gt;
&lt;li&gt;Organic Inbound: high volume and low signal - anyone can click to apply. So time consuming it often needs automation to filter and prioritize, sometimes it even needs (time delayed) auto-rejection emails.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="setting-up-the-interview-loop"&gt;Setting Up the Interview Loop&lt;/h1&gt;
&lt;p&gt;Candidates, and even Interviewers, rarely see all the machinery underpinning the Interview Loop.&lt;/p&gt;
&lt;p&gt;There's usually an existing framework or template in the company, but as a Hiring Manager you may need or want to adapt it.&lt;/p&gt;
&lt;p&gt;How many interviews, what type, and in what order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recruiter Screen: cross check their resume with reality, quick company/culture fit, scratch test on a few topics&lt;/li&gt;
&lt;li&gt;Hiring Manager Screen: is this worth everybody's time?&lt;/li&gt;
&lt;li&gt;hands-on technical: core responsibility of the role&lt;/li&gt;
&lt;li&gt;architecture: systems design and big picture thinking&lt;/li&gt;
&lt;li&gt;cross-functional: input from adjacent disciplines like Product or Quality&lt;/li&gt;
&lt;li&gt;presentation: previous project or case study&lt;/li&gt;
&lt;li&gt;simulation: a scenario that ramps up&lt;/li&gt;
&lt;li&gt;behavioral: experience, judgment, career ambitions&lt;/li&gt;
&lt;li&gt;executive: department/company culture fit, adaptability, future promotion potential&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Will you have discrete phases and gates where candidates get evaluated like Recruiter/Manager screen, Tech Screen, Onsite? Or a more continuous sequence of sessions?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Since there's an additional cost to your org for every additional session you add, consider the heuristic of increasing investment as you gain confidence and need deeper signal.&lt;/p&gt;
&lt;p&gt;And if the candidate fails on a critical area, do you continue with the rest of the sessions?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Choosing interviewers&lt;/strong&gt; is like casting for a show: you need an experienced expert for each topic, and you need their backups for availability.&lt;/p&gt;
&lt;p&gt;I've found being specific on your priority order and max number of interviews one employee should do per a week is critical to keeping things smooth and sustainable.&lt;/p&gt;
&lt;p&gt;You may also have to borrow folks from a peer's reporting chain which is a delicate conversation about priorities and "for the good of everyone by increasing the company's overall capacity".&lt;/p&gt;
&lt;h2 id="training-interviewers"&gt;Training Interviewers&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Training interviewers&lt;/strong&gt; before they actually interview an external candidate is incredibly important but always ends up so low a priority that it defaults to "learning on the job" - to the detriment of everyone involved.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bad outcomes can range from "missed a good candidate" to "waste of time" to &lt;strong&gt;"lawsuit against your organization"&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Hiring Manager should write out the minimum acceptable signal for each part of the loop. What's being used to identify good vs weak?&lt;/p&gt;
&lt;p&gt;Get clarity on what cannot be legally asked in an interview, and on when and how to discreetly end an interview if it goes off the rails.&lt;/p&gt;
&lt;p&gt;Then meet with prospective Interviewers, as a group or individually. Talk through the job req, their specific topic, time management and the expected usage of time (i.e. 5 mins intro, 35 mins on topic, 5 mins candidate questions).&lt;/p&gt;
&lt;p&gt;During this process you can and should get feedback from your experienced people about the role's requirements, what should be asked, and what really matters in terms of concrete signal.&lt;/p&gt;
&lt;p&gt;For those less experienced overall, or on a specific topic, some useful techniques are mock interviews (you play the role of a candidate) and shadowing (quietly observe in the room) an experienced interviewer's session.&lt;/p&gt;
&lt;h3 id="structured-interviews"&gt;Structured Interviews&lt;/h3&gt;
&lt;p&gt;Structured interviews (especially for subjective areas) improve the objectivity and effectiveness:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://alliancefordecisioneducation.org/podcasts/episode-022-deciding-fast-and-slow-with-dr-daniel-kahneman/"&gt;https://alliancefordecisioneducation.org/podcasts/episode-022-deciding-fast-and-slow-with-dr-daniel-kahneman/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fs.blog/job-interviews/"&gt;https://fs.blog/job-interviews/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fs.blog/knowledge-project-podcast/daniel-kahneman-2/"&gt;https://fs.blog/knowledge-project-podcast/daniel-kahneman-2/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="scheduling-challenges"&gt;Scheduling Challenges&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Scheduling&lt;/strong&gt; can be a large invisible hole in the hiring pipeline.&lt;/p&gt;
&lt;p&gt;It's hard enough to juggle your team's existing responsibilities while prioritizing the candidate's availability, but you may also have to coordinate calendars for cross-functional peers or a whole panel session.&lt;/p&gt;
&lt;p&gt;Stuff happens: someone's sick, there's a production or security incident, the candidate last-minute cancels and needs to reschedule.&lt;/p&gt;
&lt;p&gt;You'll need debrief meetings too - ideally the first ones are synchronous so you can improve on shared understanding of calibration: what was a good score, a strong candidate, and why? Any yellow or red flags and best questions that uncover those?&lt;/p&gt;
&lt;p&gt;If you're lucky there's a dedicated scheduling coordinator, fancy software that matches availability, or your recruiter is a saint and a calendar savant. You should be prepared to be involved: as hiring manager, you know your team’s calendars, constraints, and priorities better than anyone else.&lt;/p&gt;
&lt;h1 id="hiring-manager-interviews"&gt;Hiring Manager Interviews&lt;/h1&gt;
&lt;p&gt;This is a unique piece in the puzzle because no one else can really own:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Can I adequately support their professional needs and career ambitions?&lt;/li&gt;
&lt;li&gt;Will I be able to manage them? Would they listen to me and take feedback, will we get along professionally?&lt;/li&gt;
&lt;li&gt;What might I need to adapt or prepare the team for during interviewing?&lt;/li&gt;
&lt;li&gt;Do they add something special we just don't have, that I didn't even think to ask/require?&lt;/li&gt;
&lt;li&gt;Will this person thrive here?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Delegation at its finest: depending on your team and peers to evaluate role specific skills, communication, planning, etc.&lt;/p&gt;
&lt;p&gt;This is your opportunity to ask open-ended questions with follow-ups. Get them to open up so you can understand motivation, self-awareness, judgment, and professional style. How do they handle stress and accountability?&lt;/p&gt;
&lt;p&gt;The classic question is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tell me about a conflict with a peer or your manager, and how it was resolved?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One of my personal favorites:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Where do you see yourself in 2 years time, even regardless of this role?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="tech-screens-and-the-onsite"&gt;Tech Screens and the Onsite&lt;/h1&gt;
&lt;p&gt;There is already a lot of good material on designing technical interviews, so I will not try to recreate all of it here.&lt;/p&gt;
&lt;p&gt;Here are a couple that I like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.lihaoyi.com/post/HowtoconductagoodProgrammingInterview.html"&gt;https://www.lihaoyi.com/post/HowtoconductagoodProgrammingInterview.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://review.firstround.com/the-anatomy-of-the-perfect-technical-interview-from-a-former-amazon-vp/"&gt;https://review.firstround.com/the-anatomy-of-the-perfect-technical-interview-from-a-former-amazon-vp/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="debriefs-data-and-humans"&gt;Debriefs - Data and Humans&lt;/h1&gt;
&lt;p&gt;Be clear with yourself and everyone on the debrief panel:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is an attempt at deciding fit of a candidate for a role and organization at a given moment. It is not a personal or professional judgement on the individual&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Everyone is "in a room" to work towards a decision. It's not a debate, it's a fact finding mission. How much signal can you extract from the process?&lt;/p&gt;
&lt;p&gt;Scorecards appear clinical and objective, but there are many known cognitive biases to watch out for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;anchoring or recency bias (first or most recently seen as the best candidate)&lt;/li&gt;
&lt;li&gt;similarity bias (selecting for perceived similarity to the interviewer: background, interests, communication style, etc.)&lt;/li&gt;
&lt;li&gt;liking bias (charm is not the only criteria)&lt;/li&gt;
&lt;li&gt;prestige bias (brand or fame instead of focusing on evidence)&lt;/li&gt;
&lt;li&gt;stereotyping&lt;/li&gt;
&lt;li&gt;attribution error (no skill or just nervous?)&lt;/li&gt;
&lt;li&gt;groupthink&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Interviewer feedback quality will vary&lt;/strong&gt;, some people write detailed, structured scorecards with specific examples. Others write "Yes, hire".&lt;/p&gt;
&lt;p&gt;Some of your interviewers will have a really high bar, while others generally default to yes.&lt;/p&gt;
&lt;p&gt;Your role as Hiring Manager is to improve the process: set expectations and get the data.&lt;/p&gt;
&lt;p&gt;Having a concrete scoring rubric with examples converts some of the subjectivity into objective numbers. Enumerate ways for candidates to score points, and even consider transparently sharing your scoring rubric with the candidate.&lt;/p&gt;
&lt;p&gt;The longer it takes for you and your interviewers to understand what "good enough" looks like, the more likely you are to lose that window of opportunity for an amazing fit candidate.&lt;/p&gt;
&lt;p&gt;I've found it goes most smoothly if there's a clear Facilitator and each person provides their feedback with an opportunity for questions. Not a place for the "loudest voice wins", or detours into technology preferences and ideology.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It can feel like the hardest part to sit on your hands and keep your mouth shut, but you really have to listen and stay curious&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;*I have found that sometimes you can't or shouldn't cleanly tell the interview panel whether you're making an offer - you may need further discussion with your manager and may not even yourself know *&lt;/p&gt;
&lt;h2 id="the-decision"&gt;The Decision&lt;/h2&gt;
&lt;p&gt;The hiring manager makes the decision, informed by the panel. You may want unanimity. You need data. You will deal with the consequences if you hire against consensus or over objections.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you pass on someone, then maybe you'll see this person again when they're the Interviewer and you're looking for a job.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One of the most memorable debriefs I ever experienced was a group all giving a barely-good-enough score "Yes to hire". Then when I asked each person "Would you sit next to this person and work with them for the next month", an instant "No".&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="always-be-closing"&gt;Always Be Closing&lt;/h1&gt;
&lt;p&gt;Every touch from the recruiting team and interviewer is a signal about your organization. Great candidates have options and other offers.&lt;/p&gt;
&lt;p&gt;While the recruiter handles logistics and total compensation offer, you should "close" personally, because after the dust settles this person will become your direct report.&lt;/p&gt;
&lt;p&gt;Call the candidate on the phone and tell them why you want &lt;em&gt;them&lt;/em&gt; specifically. If it's appropriate, take them out to coffee or lunch, and sell them on the broader vision of the team, the work, and their growth.&lt;/p&gt;
&lt;p&gt;The goal isn't perfection or winning, it's to have a strong foundation and keep improving.&lt;/p&gt;
&lt;h2 id="the-offer-and-everything-that-can-go-wrong"&gt;The Offer and Everything That Can Go Wrong&lt;/h2&gt;
&lt;p&gt;Now that you found someone you want to hire, you can find out where things stall, fall apart, or get weird.&lt;/p&gt;
&lt;p&gt;The Total Compensation Package is usually a combination of salary and equity, hopefully part of a pre-defined compensation framework that maps to existing employee levels and salary bands.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.john-pfeiffer.com/compensation-planning-is-sudoku-with-feelings/"&gt;https://blog.john-pfeiffer.com/compensation-planning-is-sudoku-with-feelings/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Usually a number of people will need to sign off on the Offer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your manager (and maybe even someone above them): is this really what the org needs right now?&lt;/li&gt;
&lt;li&gt;HR: any discrepancies or irregularities in the interview process, background check, employment conditions, etc?&lt;/li&gt;
&lt;li&gt;Finance: this will become an ongoing expense to the business - was it really attributed/budgeted correctly?&lt;/li&gt;
&lt;li&gt;Maybe even the Founder or CEO&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In a chaotic or high friction system this can cause delays - long enough to frustrate or lose a candidate.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;"Ghosting" or never getting back to a candidate, at any stage - especially this stage, is a reputational black eye&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Comp negotiation:&lt;/strong&gt; The candidate wants $X + 20% - is that really doable? In salary band range? How does it look compared to existing team members and employees?&lt;/p&gt;
&lt;p&gt;The recruiter (and/or an internal referrer) may be advocating for the candidate.&lt;/p&gt;
&lt;p&gt;Are you willing to push your Manager/HR/Finance for this?&lt;/p&gt;
&lt;p&gt;Sometimes the first offer feels like a deliberate fiction: of course there's 5% wiggle room, counter-offer back.&lt;/p&gt;
&lt;p&gt;Similarly, be careful to not negatively judge a candidate who negotiates: hate the game not the player. Understand they have their own needs and goals - this is one of the few places where someone has leverage with their (prospective) employer.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Sometimes candidates take a long time to consider the offer. Maybe working through a competing interview loop or waiting for a counter-offer or going through a life event. Lean into your Recruiter's people skills to diplomatically reach out. And keep your process going&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Counteroffers&lt;/strong&gt; complicate things. The candidate's current employer, or a competing employer have offered them something "better".&lt;/p&gt;
&lt;p&gt;Take a pause before you act. You might not need to counter, nor resign yourself to losing the candidate.&lt;/p&gt;
&lt;p&gt;If possible discover what's compelling about the alternative: title? autonomy? equity? "More cash" can mean immediate increase or a future promise, or a larger (future) bonus.&lt;/p&gt;
&lt;p&gt;Make your case persuasively, but don't oversell or overpromise. I've hired someone at top of band and then really struggled 10 months later when they wanted a substantial raise but they didn't meet the bar for promotion.&lt;/p&gt;
&lt;h1 id="continuous-improvement"&gt;Continuous Improvement&lt;/h1&gt;
&lt;p&gt;Hiring is always a moving target: needs of the company or team change, candidates appear and disappear from the market.&lt;/p&gt;
&lt;p&gt;Having written artifacts for each part of your system means you have a transparent reference for everyone, and a place to update and roll out improvements.&lt;/p&gt;
&lt;p&gt;After a few weeks (and a lot of resumes) you should already be learning from the funnel: analyze which things aren't working?&lt;/p&gt;
&lt;p&gt;As you experiment with changes monitor using your (ATS aka applicant tracking system) dashboards.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;On a single req I've actually experienced changing almost every part from requirements to job posting title to role title and level.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When things aren't working, start from a charitable perspective and use the "blameless post-mortem" and "5 Whys" techniques.&lt;/p&gt;
&lt;p&gt;This can feel like a lot of pressure and a big decision. A bad hire impacts the team and maybe even many others in your organization, which is why so often "no hire" is the safe choice.&lt;/p&gt;
&lt;p&gt;Also though, economic or political winds may change and your job opening will be closed unfilled. It's a measure of your resilience to learn as much as you can from the experience and improve for next time.&lt;/p&gt;
&lt;p&gt;Hiring can be emotionally exhausting. Know that you too will "level up" and get better by doing it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I definitely remember the naive time I closed a req after a candidate accepted our offer, and then the day they were going to start we learned they actually decided to go with another company&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="measuring-success"&gt;Measuring Success&lt;/h2&gt;
&lt;p&gt;Of course 100% accepted offers sounds great, unless it's a sample size of 1. Or new hires don't reach the 90 day mark. And having more headcount is also more headache at annual review and compensation planning.&lt;/p&gt;
&lt;p&gt;Consider some other ways to measure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;candidate experience becomes marketing for your organization's reputation as an employer&lt;/li&gt;
&lt;li&gt;interviewers will remember if things were well organized or chaotic&lt;/li&gt;
&lt;li&gt;a well documented system elevates the process for everyone&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Great management isn't just intuition and a few good decisions. Instead, like a "sports dynasty" or a finely engineered software system, it reproducibly generates great outcomes.&lt;/p&gt;
&lt;p&gt;Hiring is part of the long lifecycle of your relationship with someone, from candidate to direct report to transferred to another team to "alumni".&lt;/p&gt;
&lt;h1 id="resources"&gt;Resources&lt;/h1&gt;
&lt;p&gt;After you hire then it's time for onboarding which is a whole different blog post. =]&lt;/p&gt;
&lt;p&gt;Here are some other good resources on Hiring:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stripe.com/guides/atlas/scaling-eng"&gt;https://stripe.com/guides/atlas/scaling-eng&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.joelonsoftware.com/2006/10/25/the-guerrilla-guide-to-interviewing-version-30/"&gt;https://www.joelonsoftware.com/2006/10/25/the-guerrilla-guide-to-interviewing-version-30/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/charlax/engineering-management?tab=readme-ov-file#hiring"&gt;https://github.com/charlax/engineering-management?tab=readme-ov-file#hiring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.developing.dev/p/meta-hiring-lead-on-behind-the-scenes"&gt;https://www.developing.dev/p/meta-hiring-lead-on-behind-the-scenes&lt;/a&gt; is Peterman's interview with Austen McDonald, a former Meta hiring committee lead who conducted hundreds of interviews.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="bonus-update-how-to-update-an-interview-loop-for-ai"&gt;Bonus Update - How to update an Interview Loop for AI&lt;/h1&gt;
&lt;p&gt;I led an update to our engineering interview loop once it was clear that engineers were effectively using AI every day to do the work.&lt;/p&gt;
&lt;p&gt;During interviews we were already getting awkward signals: candidates quietly using LLMs during coding screens, long pauses, eyes off-camera, and trouble explaining their own code.&lt;/p&gt;
&lt;p&gt;I split the loop into two parts: a non-AI fundamentals exercise first, then an AI-required round where candidates show how they actually work with prompting, evaluating generated code, adding guardrails/tests, debugging, and refactoring.&lt;/p&gt;
&lt;p&gt;That made the process far better: transparency removed the awkwardness, candidates relaxed, and some blew us away. The artifacts made debriefs much stronger too: we could evaluate judgment and approach, not just "code".&lt;/p&gt;
&lt;p&gt;With agents, the next hard question is what “bring your dev environment” should mean in an interview?&lt;/p&gt;</content><category term="leadership"/><category term="career"/><category term="engineering"/><category term="management"/><category term="leadership"/><category term="director"/><category term="recruiting"/><category term="hiring"/><category term="interviewing"/></entry><entry><title>Golang Concurrency Part 1 WaitGroup</title><link href="https://blog.john-pfeiffer.com/golang-concurrency-part-1-waitgroup/" rel="alternate"/><published>2024-04-23T22:22:00-07:00</published><updated>2024-04-23T22:22:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2024-04-23:/golang-concurrency-part-1-waitgroup/</id><summary type="html">
&lt;p&gt;When I tried to read my previous article on concurrency in Golang I felt like it tried to pack too much in so this is the same topic broken into parts, which may even leave room for more depth.
- previous article &lt;a href="https://blog.john-pfeiffer.com/golang-concurrency-goroutines-and-channels/"&gt;https://blog.john-pfeiffer.com/golang-concurrency-goroutines-and-channels/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="background-on-concurrency-and-goroutines"&gt;Background on Concurrency and …&lt;/h1&gt;</summary><content type="html">
&lt;p&gt;When I tried to read my previous article on concurrency in Golang I felt like it tried to pack too much in so this is the same topic broken into parts, which may even leave room for more depth.
- previous article &lt;a href="https://blog.john-pfeiffer.com/golang-concurrency-goroutines-and-channels/"&gt;https://blog.john-pfeiffer.com/golang-concurrency-goroutines-and-channels/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="background-on-concurrency-and-goroutines"&gt;Background on Concurrency and Goroutines&lt;/h1&gt;
&lt;p&gt;Goroutines are like lightweight threads. This removes some of the overhead of attempting to use concurrency with OperatingSystem threads.
Here is someone else's better explanation of threads&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=oV9rvDllKEg"&gt;https://www.youtube.com/watch?v=oV9rvDllKEg&lt;/a&gt; Rob Pike describing Concurrency&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Thread_(computing)"&gt;https://en.wikipedia.org/wiki/Thread_(computing)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/4_Threads.html"&gt;https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/4_Threads.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="goroutines"&gt;Goroutines&lt;/h1&gt;
&lt;p&gt;Normally in programs main executes sequentially from top to bottom.
Go routines can operate concurrently to main (and any other go routines). It only takes the simple syntax of prepending the keyword "go". &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sleep then count"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://go.dev/play/p/KX-bMG0KBBu"&gt;https://go.dev/play/p/KX-bMG0KBBu&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;This code example highlighted that when main exits all goroutines also exit, even if they have not completed.&lt;/p&gt;
&lt;h1 id="golang-waitgroup"&gt;Golang WaitGroup&lt;/h1&gt;
&lt;p&gt;In order to add control over the goroutines there are many tools, the simplest is WaitGroup. &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"sync"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;exampleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WaitGroup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WaitGroup&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;exampleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://go.dev/play/p/YYJVC36uB5r"&gt;https://go.dev/play/p/YYJVC36uB5r&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The whole program executes in 3 seconds: even as sequentially things take 2 seconds, the next 2 sleep statements occur concurrently.&lt;/p&gt;
&lt;p&gt;A WaitGroup must in advance be passed a count that matches every execution of "Done()" (usually by goroutines).&lt;/p&gt;
&lt;p&gt;Even though the anonymous function that wraps "example()" and "exampleAsync()" both have a 1 second sleep statement, the output shows they run concurrently.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The anonymous function in the middle shows how to pass a string parameter, and also that the waitgroup variable is available through "closure".&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://go.dev/tour/moretypes/25"&gt;https://go.dev/tour/moretypes/25&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gobyexample.com/closures"&gt;https://gobyexample.com/closures&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;For readability, maintainability, and re-use most people write a separate function rather than using anonymous functions.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Passing the waitgroup by reference is safe as it is designed for coordinating goroutines, and the "defer" keyword just ensures that just as the function exits that statement will immediately execute.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"sync"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WaitGroup&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is a common gotcha where the program will deadlock as the WaitGroup forever expects one more "Done()" than the code provides.&lt;/p&gt;</content><category term="programming"/><category term="go"/><category term="golang"/><category term="goroutines"/><category term="concurrency"/><category term="waitgroup"/></entry><entry><title>One of the Hardest Parts of the Job - Leading Through Layoffs</title><link href="https://blog.john-pfeiffer.com/one-of-the-hardest-parts-of-the-job-leading-through-layoffs/" rel="alternate"/><published>2024-02-29T12:01:00-08:00</published><updated>2024-02-29T12:01:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2024-02-29:/one-of-the-hardest-parts-of-the-job-leading-through-layoffs/</id><summary type="html">
&lt;p&gt;&lt;em&gt;Disclaimer: this piece reflects my experiences and is not legal or professional advice.&lt;/em&gt;&lt;/p&gt;
&lt;h1 id="it-happens"&gt;It happens&lt;/h1&gt;
&lt;p&gt;I've been through multiple layoffs: some I delivered, some I narrowly avoided being on the receiving end. It is never easy, never painless.&lt;/p&gt;
&lt;p&gt;This can happen to any kind of company: a startup that runs …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;&lt;em&gt;Disclaimer: this piece reflects my experiences and is not legal or professional advice.&lt;/em&gt;&lt;/p&gt;
&lt;h1 id="it-happens"&gt;It happens&lt;/h1&gt;
&lt;p&gt;I've been through multiple layoffs: some I delivered, some I narrowly avoided being on the receiving end. It is never easy, never painless.&lt;/p&gt;
&lt;p&gt;This can happen to any kind of company: a startup that runs out of runway or has to pivot, or a large company making a "strategic change." Sometimes it's a broader shock like the 2008 financial crisis or the COVID-19 pandemic.&lt;/p&gt;
&lt;p&gt;Even if it hasn't happened directly to you, it has probably happened to someone close to you: a partner, a family member, a close friend, or a former colleague.&lt;/p&gt;
&lt;h2 id="stabilize-yourself-first"&gt;Stabilize yourself first&lt;/h2&gt;
&lt;p&gt;During layoffs, start by acknowledging it is understandable that you're going to have feelings about this.&lt;/p&gt;
&lt;p&gt;A useful parallel is the Kubler-Ross model of stages of grief. The point is your emotional state affects your judgment, your patience, and your capacity for the hard decisions and difficult conversations ahead.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Adapted Kubler-Ross stages of grief" src="../images/adapted-kubler-ross-stages.png"/&gt;&lt;/p&gt;
&lt;p&gt;Shock and denial: "Maybe we don't really need to do this. Maybe Q4 will turn around." &lt;/p&gt;
&lt;p&gt;Then frustration and anger: at the situation, at the leadership decisions that led to this, at yourself for not seeing it sooner. Feeling like some performance issue could have been addressed earlier.&lt;/p&gt;
&lt;p&gt;Bargaining: maybe people can work extra on weekends, or temporarily take pay cuts, and the problem will go away.&lt;/p&gt;
&lt;p&gt;Then a low period where everything feels heavy, normal work feels impossible.&lt;/p&gt;
&lt;p&gt;As these emotions play out, don't make decisions yet, as your judgment isn't reliable. Instead gather facts and prepare to think clearly.&lt;/p&gt;
&lt;p&gt;At a high level, consider 3 phases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Planning:&lt;/strong&gt; understand the problem, gather data, plan with options&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decision:&lt;/strong&gt; commit and follow through&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Integration:&lt;/strong&gt; help the new organization stabilize and become healthy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;This is one of the ultimate tests of your capacity for confidentiality: leaking your thoughts and emotions early will create anxiety and chaos.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="managing-up"&gt;Managing Up&lt;/h3&gt;
&lt;p&gt;This is an advanced career move, so not for everyone, (and be mindful of your emotional state if you decide to attempt this) but if you have built up enough trust, double check with your manager:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is this really necessary? (These kinds of changes can have unintended side effects, can lead to irrevocable loss of morale)&lt;/li&gt;
&lt;li&gt;Will this be multiple rounds? (longer timeframes means more paralyzing and demoralizing)&lt;/li&gt;
&lt;li&gt;Who else will be involved in the planning, and can I discuss it with them?&lt;/li&gt;
&lt;li&gt;Do you already have a list in mind?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Be very careful to not sound like you're questioning leadership; seek to understand so you can achieve the best outcome.&lt;/p&gt;
&lt;p&gt;Going through these questions may help you have more complete answers and be anchored as you begin the difficult work.&lt;/p&gt;
&lt;h1 id="planning-work-backwards-from-the-goal"&gt;Planning: Work Backwards from the Goal&lt;/h1&gt;
&lt;p&gt;If the problem is bad enough that it requires a reduction in force, waiting months or even weeks is unlikely to improve things.&lt;/p&gt;
&lt;p&gt;Urgency doesn't mean reckless. These are some of the most critical, life-impacting decisions you will make.&lt;/p&gt;
&lt;p&gt;Prioritize thinking and working on this before almost anything else - the people affected deserve the best from you. Do it in a quiet, private space.&lt;/p&gt;
&lt;p&gt;To be clear: you need a healthy organization of teams at the end to get good results.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Start with the outcome: what does the successful organization look like after?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Work backwards from there...&lt;/p&gt;
&lt;h2 id="what-does-the-future-org-look-like"&gt;What does the Future Org look like?&lt;/h2&gt;
&lt;p&gt;An understandable misstep is to focus on who to remove rather than what you're tasked with building.&lt;/p&gt;
&lt;p&gt;Don't start from your existing org, it may have grown organically over time or hastily during a hiring boom. Think outside the box. Start with answering key questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What are the most critical projects that have to continue?&lt;/li&gt;
&lt;li&gt;Are there natural domains or boundaries around the key initiatives?&lt;/li&gt;
&lt;li&gt;What skills/support are most needed?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Conway's Law points out that the product (or software) will mirror the organization that builds it. &lt;a href="https://en.wikipedia.org/wiki/Conway%27s_law"&gt;https://en.wikipedia.org/wiki/Conway%27s_law&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Similarly, when you understand the shape of the future work, it should inform how you shape your new organization.&lt;/p&gt;
&lt;p&gt;A RIF is an unpleasant forcing function: consider the really painful and radical ideas first, sometimes they're right and dismissing them limits your options.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Look to create teams that own clear domains with minimal external coordination&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;prefer clear end-to-end autonomy and value delivery&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Consider retaining deep expertise, but balance it against future potential&lt;/li&gt;
&lt;li&gt;Prioritize high morale and high productivity: the road ahead will be bumpy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then layer in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Are there clearly areas that are lower priority/impact?&lt;/li&gt;
&lt;li&gt;Places the business can no longer justify?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Consider your ideal team size: often less than four is fragile (PTO, sickness, on-call coverage), and greater than ten struggles to focus (mega standups, orthogonal sub-projects). &lt;/p&gt;
&lt;p&gt;Use a confidential whiteboard; brainstorm and be open to a lot of paths, options, and variations.&lt;/p&gt;
&lt;p&gt;I remember talking with my counterparts in other disciplines and departments. Having so many hard conversations about what they were seeing and thinking about. Drawing and redrawing so many variations to not miss a possible improvement.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;And some more resources on org planning:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://se-radio.net/2024/02/se-radio-601-han-yuan-on-reorganizations/"&gt;https://se-radio.net/2024/02/se-radio-601-han-yuan-on-reorganizations/&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://sfelc.com/podcasts/how-to-do-an-effective-re-org-aaron-erickson-orgspace-mike-tria-atlassian"&gt;https://sfelc.com/podcasts/how-to-do-an-effective-re-org-aaron-erickson-orgspace-mike-tria-atlassian&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h1 id="gathering-data"&gt;&lt;strong&gt;Gathering Data&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;Gather data, both objective and subjective, into one confidential place.&lt;/p&gt;
&lt;p&gt;You will need enough data to show yourself that you were thorough. To show your leadership that you were rigorous. And to help you be confident and thoughtful in the difficult conversations ahead.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What is the driver of the RIF: financial, strategic, or something else?&lt;/li&gt;
&lt;li&gt;Every person's current tenure and salary&lt;/li&gt;
&lt;li&gt;Performance Reviews and OKRs&lt;/li&gt;
&lt;li&gt;Previously created succession plans&lt;/li&gt;
&lt;li&gt;Past, present, and future Roadmaps. Plus agile sprint data or charts&lt;/li&gt;
&lt;li&gt;An inventory of all responsibilities, special skills, critical external relationships&lt;/li&gt;
&lt;li&gt;Emotional data can look like "Which 1:1s do you dread? Which do you look forward to?"&lt;/li&gt;
&lt;li&gt;Who's engaged in meetings, taking initiative, amenable to coaching?&lt;/li&gt;
&lt;li&gt;Who's been actively cross-training, and is adaptable enough to do more?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="building-careful-systems-of-evaluation"&gt;Building Careful Systems of Evaluation&lt;/h2&gt;
&lt;p&gt;Consider starting with a simple table of every name.&lt;/p&gt;
&lt;p&gt;Add columns of attributes: Productivity, Cost, etc.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Customize to the attributes that make the most sense to you and your org&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Stack-rank the attributes by priority (or add "weightings") - not all data is equal.&lt;/p&gt;
&lt;p&gt;Then for each person, add a score of 1 to 5 for each cell: this pseudo-numerical system isn't meant to absolve you of the fact you're making the decisions, but to force you to recognize what you're prioritizing.&lt;/p&gt;
&lt;p&gt;I found using positive attributes in the scoring was difficult because I wanted to give everyone a top score.&lt;/p&gt;
&lt;p&gt;Looking at negative attributes is more uncomfortable, but also more revealing in this context. It forces you to face the difficult tradeoffs when the work ahead is about to get harder.&lt;/p&gt;
&lt;p&gt;Example Table (high score is worse)&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;NAME&lt;/th&gt;
&lt;th&gt;DISRUPTIVE,&lt;/th&gt;
&lt;th&gt;LOWER PRODUCTIVITY,&lt;/th&gt;
&lt;th&gt;NEEDS SKILLS DEVELOPMENT,&lt;/th&gt;
&lt;th&gt;REQUIRES MANAGER ENERGY,&lt;/th&gt;
&lt;th&gt;COST,&lt;/th&gt;
&lt;th&gt;SUM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Charlie&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Then iterate again on all the values. Sort and re-sort from different angles.&lt;/p&gt;
&lt;p&gt;Next try adding a "safe list" approach: start with the nucleus of your future successful org. (Move them to a separate spreadsheet).&lt;/p&gt;
&lt;p&gt;Consider second-order effects: who might be demoralized or leave due to the layoffs.&lt;/p&gt;
&lt;p&gt;Like when interviewing a great candidate who doesn't quite fit: this is more about the future organization, its priorities, and possible financial constraints than the individual's qualifications.&lt;/p&gt;
&lt;p&gt;Also future team chemistry matters as those remaining need to pull together and push forward.&lt;/p&gt;
&lt;p&gt;Will you need as many managers? How will new reporting structures work?&lt;/p&gt;
&lt;p&gt;Never delude yourself that the system will give you the answers and absolve you of the decision making responsibility.&lt;/p&gt;
&lt;p&gt;Listen to your instincts, not because they're always right, but because maybe they're telling you something you don't fully understand yet.&lt;/p&gt;
&lt;p&gt;Spend time reflecting and getting more data when something feels off.&lt;/p&gt;
&lt;h2 id="know-the-limits-of-what-you-know"&gt;Know the limits of what you know&lt;/h2&gt;
&lt;p&gt;Having a really complex system and fancy models doesn't mean you can predict the future. Often too much complexity gets in the way.&lt;/p&gt;
&lt;p&gt;And here's a really hard thing: do not involve an individual's personal situation.&lt;/p&gt;
&lt;p&gt;You can tie yourself in knots thinking about each person's life consequences. Worse, you likely don't know everyone's full situation: family and dependents, expecting children, mortgage and debt, immigration status, etc.&lt;/p&gt;
&lt;p&gt;If you start making exceptions where will you stop? Is it fair to the people whose situations you don't know?&lt;/p&gt;
&lt;p&gt;The professional role of a people manager is evaluating people's professional circumstances.&lt;/p&gt;
&lt;h2 id="another-evaluation-framework"&gt;Another Evaluation Framework&lt;/h2&gt;
&lt;p&gt;Another useful framework comes from criteria Jim Collins lays out in "Good to Great":&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How would you feel if the person quit today?&lt;/li&gt;
&lt;li&gt;Do you risk losing others by keeping this person?&lt;/li&gt;
&lt;li&gt;Are there concerns about their values, willpower to get things done, or skills?&lt;/li&gt;
&lt;li&gt;When things go wrong, do they look to blame, or in the mirror to improve? &lt;/li&gt;
&lt;li&gt;Have they been giving you signals like "this is just a job"?&lt;/li&gt;
&lt;li&gt;Do you have them in the right seat on the bus?&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Did your bus just get a lot shorter?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Do not be surprised if you are also on a list, even if it's not today&lt;/em&gt;&lt;/p&gt;
&lt;h1 id="get-feedback"&gt;Get Feedback&lt;/h1&gt;
&lt;p&gt;You are not doing this in a vacuum. All of your research and planning is part of a larger process.&lt;/p&gt;
&lt;p&gt;As you reach out to people for feedback, remember to be careful: this is "need to know" only. Only bounce ideas off people you can trust with confidentiality.&lt;/p&gt;
&lt;p&gt;Consider having some meetings "off calendar". Be cautious about how you share any lists or plans. Premature sharing can lead to rapid and uninformed decisions.&lt;/p&gt;
&lt;h2 id="an-open-mind-for-the-best-outcome"&gt;An open mind for the best outcome&lt;/h2&gt;
&lt;p&gt;Do be open-minded to different viewpoints. This is about the best possible outcome, not your ego.&lt;/p&gt;
&lt;p&gt;Sometimes saying something out loud reveals a very good, or very bad, idea.&lt;/p&gt;
&lt;p&gt;Can you be vulnerable and admit if you have favorites? Why might you feel a certain way?&lt;/p&gt;
&lt;p&gt;Before you finalize anything: has someone spotted an unconscious bias? Are there unique permissions or access you may have missed?&lt;/p&gt;
&lt;p&gt;What's your second-tier succession plan, and how will you cover if others quit?&lt;/p&gt;
&lt;p&gt;How will the chemistry change between teams and groups?&lt;/p&gt;
&lt;p&gt;Are there responsibilities someone in your group can take over elsewhere in the org? Could there be trades with other managers?&lt;/p&gt;
&lt;p&gt;These extra conversations require discretion but can sometimes help retain one more person; this can make all the difference.&lt;/p&gt;
&lt;hr/&gt;
&lt;h1 id="decisions"&gt;&lt;strong&gt;Decisions&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;Once you've made the decision, stop "looking for more signs" to confirm or deny it. You've done the work.&lt;/p&gt;
&lt;p&gt;Do stay open to truly last-minute information: someone resigning or otherwise exiting, a material change in the company's plans or needs.&lt;/p&gt;
&lt;p&gt;You still need to keep this all confidential, which means trying to avoid a change in behavior or hints. The hardest part is yet to come.&lt;/p&gt;
&lt;h2 id="logistics"&gt;Logistics&lt;/h2&gt;
&lt;p&gt;Figure out when the decision is final. From there, figure out when is the soonest you can deliver the news well. Note that HR, Legal, Admin, IT, and other departments will need some time to prep too.&lt;/p&gt;
&lt;p&gt;Try to keep the gap short: days, not weeks. Long delays create anxiety, speculation, and distraction.&lt;/p&gt;
&lt;p&gt;Plan to do it all at once. This may take most of a day, or more. The decision maker should deliver the news privately to each person affected. Be thoughtful of how many people are "in the room" when someone receives news like this.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If it's not possible to do in person, make sure IT is ready for the series of private video meetings.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In many places, leaders prefer to communicate layoffs at the end of the week, with no further work expected that day, so people have private time to process. They should not be expected to absorb life-changing news and then return to a normal workday.&lt;/p&gt;
&lt;p&gt;That also means everyone supporting the process should be ready for immediate next steps once the conversations start.&lt;/p&gt;
&lt;h3 id="terminating-access"&gt;Terminating Access&lt;/h3&gt;
&lt;p&gt;The goal is not to treat people as untrustworthy. Once someone has been told they are leaving, the organization needs a clean and immediate transition point, especially on a day that may already be emotional and chaotic.&lt;/p&gt;
&lt;p&gt;This reduces confusion, avoids accidental mistakes, and makes it clear who is responsible for what from that moment on.&lt;/p&gt;
&lt;p&gt;Coordinate with IT, HR, Legal, and others on things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Production Systems, Production Data&lt;/li&gt;
&lt;li&gt;Confidential or Sensitive info&lt;/li&gt;
&lt;li&gt;Public-facing communication channels used on behalf of the organization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the day of, this may expose gaps in your preparation. In that uncomfortable event, it is better to reduce activity and access, then review it more thoroughly when things are less busy.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If someone needs to keep limited access for a short time, that should be a rare, intentional exception with a clear reason behind it.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="who-knows-when"&gt;Who Knows When&lt;/h3&gt;
&lt;p&gt;Keep this confidential from everyone inside (and outside) the company who doesn't absolutely need to know.&lt;/p&gt;
&lt;p&gt;Sync with your peers and HR who are planning and handling the changes. You're all designing the future successful org. They're your support group during this difficult time.&lt;/p&gt;
&lt;p&gt;Rumors may already be swirling. Don't be rushed. You may have to honestly say "I don't have any answers for you right now".&lt;/p&gt;
&lt;h1 id="communications-plan-and-delivery"&gt;Communications Plan and Delivery&lt;/h1&gt;
&lt;p&gt;Have a &lt;strong&gt;Communications Plan&lt;/strong&gt;. Each sentence and every word should be carefully reviewed.&lt;/p&gt;
&lt;p&gt;During layoffs, this should not be communicated as a personal criticism, or framed like a performance review. There are a lot of factors that go into the decisions. Make sure your language doesn't imply that it's about performance.&lt;/p&gt;
&lt;p&gt;For instance, if the company is no longer doing hardware, then excellent people who are experts in hardware will be let go.&lt;/p&gt;
&lt;p&gt;Plan down to the minute which people or teams are notified. Prioritize informing those most affected first.&lt;/p&gt;
&lt;p&gt;Double-check that the formal parts are ready: this is an extremely emotional one-way door. Not the time for administrative mistakes.&lt;/p&gt;
&lt;p&gt;What answers have HR prepared for handling situations like visa issues, parental leave, or other specific circumstances with compassion?&lt;/p&gt;
&lt;p&gt;Triple-check your understanding of the new org structure, including connections to other teams, which responsibilities must transition, and especially passwords, credentials, and external contacts.&lt;/p&gt;
&lt;p&gt;Prepare a FAQ: what are people most likely to ask or be worried about?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Will those leaving get severance and benefits?&lt;/li&gt;
&lt;li&gt;Am I allowed to reach out to folks personally?&lt;/li&gt;
&lt;li&gt;Who made the decisions?&lt;/li&gt;
&lt;li&gt;Why is this happening?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rehearse and practice your delivery: both to those who will be departing, and the message you'll be giving to those who remain.&lt;/p&gt;
&lt;h2 id="the-day-of"&gt;The Day Of&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Try and be rested beforehand: this is a really sad day and takes a lot out of you emotionally.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Dedicate a whole day to delivery. Nothing else on your calendar. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;There will be no productive work this day. Accept it.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="for-those-departing"&gt;For those departing&lt;/h3&gt;
&lt;p&gt;If someone is out on vacation or medical leave, delaying communication can create its own harms: they may hear a distorted version secondhand, lose time to process next steps, or be left in limbo while others already know.&lt;/p&gt;
&lt;p&gt;In practice, this is usually something to handle carefully with HR and Legal so you balance timely communication with compassion, compliance, and the person's circumstances.&lt;/p&gt;
&lt;p&gt;For each person, consider getting approval for a personal note to the official messaging - acknowledging a past success or shared memory.&lt;/p&gt;
&lt;p&gt;This is a serious moment: if possible try to avoid the nervous instinct to use humor or forced warmth. It is better to come across as calm than cheerful or casual. And even if it tears you up inside with sadness, you should try to remain composed. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stay on script&lt;/strong&gt;: be empathetic but don't argue or explain. This is about them, not you.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Offer to assist in the job search if you mean it. Don't offer if you don't mean it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;HR will take over logistics. That's usually the end of your part of the conversation.&lt;/p&gt;
&lt;h3 id="for-those-remaining"&gt;For those remaining&lt;/h3&gt;
&lt;p&gt;This is an organization-level event.&lt;/p&gt;
&lt;p&gt;A common pattern is a leadership meeting, either just before or immediately after, to explain the change at a high level to the whole company.&lt;/p&gt;
&lt;p&gt;The individual notices are often followed by a department-level meeting, then even by group and team.&lt;/p&gt;
&lt;p&gt;Finally, as a manager, be available for any ICs who may have immediate questions or concerns.&lt;/p&gt;
&lt;p&gt;Things you have to clearly explain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The official reason, and the official messaging to users/customers/partners&lt;/li&gt;
&lt;li&gt;The new organizational structure&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;What, if any, are the new connections to other teams and departments&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Any critical changes in responsibility&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Remember to have a consistent message across the many modes: verbal, written, and visual&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There will be reactions so consider in advance what's allowable (venting, tears, needing to leave early) versus what's never acceptable (no matter the circumstances).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shock and some abnormal behavior&lt;/li&gt;
&lt;li&gt;Denial, anger, frustration, bargaining, etc&lt;/li&gt;
&lt;li&gt;Survivor guilt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Refer back to your comms plan and FAQ.&lt;/p&gt;
&lt;p&gt;Tell the truth. Be honest but kind. The truth sometimes is "I can't share that" or "this is not the time to discuss that."&lt;/p&gt;
&lt;p&gt;Don't pretend to have answers you don't have. Don't over-explain or re-litigate decisions.&lt;/p&gt;
&lt;h2 id="integrating-the-day-after-and-moving-forward"&gt;Integrating - The Day After and Moving Forward&lt;/h2&gt;
&lt;p&gt;The day after: &lt;strong&gt;This day too, there will be little meaningful work accomplished.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Understand that trust has been broken. People joined and believed they were all part of a bigger mission. Your role is rebuilding that trust.&lt;/p&gt;
&lt;p&gt;The days and weeks after will continue to be challenging while you support those who remain - people can't immediately return to "normal" as everything has changed.&lt;/p&gt;
&lt;p&gt;Continue to leverage the communication plan. Patiently explain the mission ahead, the new org chart.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Actively listen.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Did you or the organization make some small mistakes that you can fix?&lt;/li&gt;
&lt;li&gt;Is there someone more affected than you anticipated?&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Who else is a flight risk? What are possible mitigations?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The weeks after will bring more hard conversations, often in 1:1s.&lt;/p&gt;
&lt;p&gt;The goal is not to have a reorg, and then lose the rest of your org.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your job is to carefully steer people to look ahead, to integrate and re-engage.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Be careful of the trap of explaining the same thing too many times, especially the hypothetical "what if" scenarios, or arguing with decisions that are now in the past.&lt;/p&gt;
&lt;h3 id="the-instinct-to-pull-away"&gt;The Instinct to Pull Away&lt;/h3&gt;
&lt;p&gt;You may find yourself shrinking back from 1:1s and skip-levels. Partly because of hard conversations and feedback, partly because it hurts to invest in relationships with people you might have to let go someday.&lt;/p&gt;
&lt;p&gt;Resist the instinct to pull away.&lt;/p&gt;
&lt;p&gt;But also don't overcorrect by smothering people with attention. Keep the same stable, professional, compassionate cadence you've always had.&lt;/p&gt;
&lt;p&gt;People managers represent both change and stability. In moments like these, your steadiness matters more than your words.&lt;/p&gt;
&lt;p&gt;Lean into directing thoughts and conversations towards experimenting: trying the new structure, learning to work in the new situation. The goal is eventually integrating the changes into a renewed sense of purpose.&lt;/p&gt;
&lt;h2 id="learnings"&gt;Learnings&lt;/h2&gt;
&lt;p&gt;Every scar should bring you some wisdom.&lt;/p&gt;
&lt;p&gt;Layoffs taught me many lessons: not only about how to try to avoid layoffs, but also about how to proactively build a healthier organization.&lt;/p&gt;
&lt;p&gt;As a people leader, strive for transparency around company goals and the company's financial situation. Be accountable, and hold others accountable, for delivering outputs and successful outcomes.&lt;/p&gt;
&lt;p&gt;Public OKRs help. So do written records of productivity: roadmaps, demos, features shipped, and concrete work output.&lt;/p&gt;
&lt;p&gt;I know how I've felt: I should have spotted performance issues earlier. There was a lot going on, but that's not an excuse. When I saw the business miss its targets, I could have escalated sooner, privately to the CEO, or maybe even to the Board.&lt;/p&gt;
&lt;p&gt;I realized I should have pushed harder during an earlier "small reorg" to avoid having multiple rounds of cuts. I wish I had pushed for certain structural changes months earlier.&lt;/p&gt;
&lt;p&gt;Did we really need to hire so fast? Were we consistent and thorough in checking on successful onboarding, integration, and the performance of new hires?&lt;/p&gt;
&lt;p&gt;Were those hires connected to future revenue, or was I "really busy hiring", and only narrowly seeing the increase in my span of control?&lt;/p&gt;
&lt;p&gt;You can't reorg and RIF, over and over, to create success. Happy, paying customers are how you "win".&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Be conscious and deliberate with your org design.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Teams with clear identity and purpose make it clearer where work maps to business needs. Teams should refresh their purpose every six months toward the highest-value work. Org resilience comes from overlap and cross-training.&lt;/p&gt;
&lt;p&gt;Cost-based modeling of teams mapped to revenue helps. A table of all salaries (and history of raises and promotions) helps.&lt;/p&gt;
&lt;p&gt;Promotions need to mean expanded expectations, not just new titles. Do regular succession planning.&lt;/p&gt;
&lt;p&gt;Performance review may mean separating earlier with people who aren't working out. Care personally, clarity is kindness.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.radicalcandor.com/blog/how-to-fire-someone"&gt;https://www.radicalcandor.com/blog/how-to-fire-someone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Never get overconfident about cash in the bank and projected revenues... especially without accounting for debt, funding cliffs, and financial risks you don't control.&lt;/p&gt;
&lt;p&gt;Consider how to carefully manage up: is the strategy clear and well understood? Are you seeing distraction, dysfunction, performance issues, or accountability gaps in the org?&lt;/p&gt;
&lt;h1 id="leadership"&gt;Leadership&lt;/h1&gt;
&lt;p&gt;Do not ever think this is someone else's responsibility. Not making a decision... is a decision.&lt;/p&gt;
&lt;p&gt;You will need to look every person you let go in the eye and own that you made the choice.&lt;/p&gt;
&lt;p&gt;Nobody wants to be in this situation, but someone has to make hard decisions, and make them well.&lt;/p&gt;
&lt;p&gt;Be honest with yourself and your leadership that some mistakes likely occurred to get you here. Be humble enough to recognize you will still make mistakes in the future too.&lt;/p&gt;
&lt;p&gt;Sometimes this moment is a crucible, a shared bond.&lt;/p&gt;
&lt;p&gt;Plan ahead how you'll support folks who are leaving. They carry your company's reputation to the outside world.&lt;/p&gt;
&lt;p&gt;Know how you'll support folks who remain. This is one of the moments where leadership happens, or doesn't.&lt;/p&gt;
&lt;h2 id="further-resources"&gt;Further Resources&lt;/h2&gt;
&lt;p&gt;Manager Tools have a number of really good podcasts on this challenging topic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.manager-tools.com/2005/10/compassionate-layoffs-hall-fame-guidance"&gt;https://www.manager-tools.com/2005/10/compassionate-layoffs-hall-fame-guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manager-tools.com/2019/03/layoff-signs-and-what-do-about-them-part-2"&gt;https://www.manager-tools.com/2019/03/layoff-signs-and-what-do-about-them-part-2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manager-tools.com/2009/03/layoff-communications-chapter-1-openly-confidential"&gt;https://www.manager-tools.com/2009/03/layoff-communications-chapter-1-openly-confidential&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="leadership"/><category term="career"/><category term="engineering"/><category term="management"/><category term="leadership"/><category term="director"/><category term="navigating change"/></entry><entry><title>Compensation Planning is Sudoku with Feelings</title><link href="https://blog.john-pfeiffer.com/compensation-planning-is-sudoku-with-feelings/" rel="alternate"/><published>2023-11-25T12:34:00-08:00</published><updated>2023-11-25T12:34:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2023-11-25:/compensation-planning-is-sudoku-with-feelings/</id><summary type="html">
&lt;blockquote&gt;
&lt;p&gt;Compensation planning is like "extra hard Sudoku" except once you've figured it all out... you still have to convince each square that it's the right solution&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Always challenging, never a straightforward "satisfying" outcome.&lt;/p&gt;
&lt;h1 id="what-is-compensation-planning-and-why-do-you-care"&gt;What is compensation planning and why do you care?&lt;/h1&gt;
&lt;p&gt;Every organization has some fixed budget. Yes there's …&lt;/p&gt;</summary><content type="html">
&lt;blockquote&gt;
&lt;p&gt;Compensation planning is like "extra hard Sudoku" except once you've figured it all out... you still have to convince each square that it's the right solution&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Always challenging, never a straightforward "satisfying" outcome.&lt;/p&gt;
&lt;h1 id="what-is-compensation-planning-and-why-do-you-care"&gt;What is compensation planning and why do you care?&lt;/h1&gt;
&lt;p&gt;Every organization has some fixed budget. Yes there's a little wiggle room, but there's also a breaking point.&lt;/p&gt;
&lt;p&gt;Compensation is "buying time with salaries": when you are in charge of an organization you owe it to them, to everyone, to spend responsibly. &lt;/p&gt;
&lt;p&gt;It is not sustainable to spend everything today and having nothing for tomorrow. Leadership is providing some measure of predictability.&lt;/p&gt;
&lt;p&gt;You must devote at least a couple of hours (or days) to planning and budgeting your largest expense of your most precious resource.&lt;/p&gt;
&lt;p&gt;So you have N people, and you have budget X. And if you mess it up really badly you'll either have 0 people or 0 dollars (actually you can go negative but that's very unpleasant).&lt;/p&gt;
&lt;h1 id="fundamentals-of-compensation-planning"&gt;Fundamentals of compensation planning&lt;/h1&gt;
&lt;p&gt;Classically you keep salaries (the majority of spending at most successful companies) separate from "everything else" (real estate, vendors, infrastructure). Start with last month's total payroll x 12. That's your baseline spend.&lt;/p&gt;
&lt;p&gt;Ask your HR or Board for clarity on any existing or historical policies on how this has been handled previously (or should be handled going forward).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Usually you are not doing this in complete isolation&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="what-your-organization-needs"&gt;What your organization needs&lt;/h2&gt;
&lt;p&gt;Before you start planning, make sure you first learn what your organization needs for the coming year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ask your manager&lt;/li&gt;
&lt;li&gt;Ask your CEO&lt;/li&gt;
&lt;li&gt;Ask your CFO&lt;/li&gt;
&lt;li&gt;Ask your Board&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There will be conflicting, even contradictory answers. That's okay =]&lt;/p&gt;
&lt;p&gt;Everyone's predicting the future, and that's hard.&lt;/p&gt;
&lt;p&gt;Ideally there will be a theme, and finding overlaps gives you a direction/pattern.&lt;/p&gt;
&lt;p&gt;Is this a growth year with hiring? Maybe it's a "hold tight" year with a slightly shrinking budget.&lt;/p&gt;
&lt;p&gt;Even in a shrinking budget there may be projects or opportunities the organization is willing to invest in.&lt;/p&gt;
&lt;p&gt;Understand what your company wants to pay for, and where your team members (skills, experience, etc.) may fit in that picture.&lt;/p&gt;
&lt;p&gt;Identify which key people (in priority order) would most impact the company if they left. Have you done succession planning?&lt;/p&gt;
&lt;p&gt;More importantly: which people have a track record of success and you believe represent the future of your organization?&lt;/p&gt;
&lt;h2 id="what-your-people-need"&gt;What your people need&lt;/h2&gt;
&lt;p&gt;Gather your data about your people and their compensation history&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When were they hired?&lt;/li&gt;
&lt;li&gt;When were they promoted within your organization?&lt;/li&gt;
&lt;li&gt;When have they received raises?&lt;/li&gt;
&lt;li&gt;Has there been a trend to their compensation (plateau, spikes, exponentially up)?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Gather your data (hopefully from your regular one-on-ones) about what your people most prioritize.&lt;/p&gt;
&lt;p&gt;People want different things (either stated or revealed preferences), so have private notes of their priority ranked list of these things...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cold hard cash&lt;/li&gt;
&lt;li&gt;equity&lt;/li&gt;
&lt;li&gt;bigger bonus when things go well&lt;/li&gt;
&lt;li&gt;flexibility in working hours&lt;/li&gt;
&lt;li&gt;flexibility in location/remote&lt;/li&gt;
&lt;li&gt;more PTO&lt;/li&gt;
&lt;li&gt;more autonomy and/or responsibilities in their work&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="some-example-personas"&gt;Some Example Personas&lt;/h3&gt;
&lt;p&gt;Alice was hired 4 years ago and prefers cash above all else. She has had regular raises every year at 3%. She's still at the low end of the salary band for her level - and you really want to retain her steady troubleshooting skills and institutional knowledge.&lt;/p&gt;
&lt;p&gt;Bob was hired 2 years ago and prefers equity and working remote. He's only had one raise during a promotion of 7%. His performance is a roller coaster, and even though he's recently had a promotion he said he thinks he's under-levelled.&lt;/p&gt;
&lt;p&gt;Charlie was hired 3 years ago and has never had a raise. Charlie likes PTO. Charlie's well liked by everyone. He's also at the top of his salary band and privately co-workers wonder if he's pulling his weight.&lt;/p&gt;
&lt;h1 id="good-practices"&gt;Good Practices&lt;/h1&gt;
&lt;p&gt;Having distinct levels with responsibilities in a career ladder is a data driven approach to evaluating performance and promotions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;there are many career ladders published on GitHub - consider using even a most rudimentary version of just two categories: "new grad" and "senior"&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You should strive to be fair and objective. Not just because it's the right thing to do - but because that's a reproducible framework that creates predictable outcomes.&lt;/p&gt;
&lt;p&gt;Salary bands for a given level are a common practice and available through a variety of sources. (see Radford's data or Milkovich et al.'s "Compensation")&lt;/p&gt;
&lt;p&gt;If you don't have salary bands you may have trouble recognizing that someone is underpaid (or overpaid) compared to market benchmarks.&lt;/p&gt;
&lt;p&gt;Remove your emotions from the process. There is a place for emotions, but not in the current calculus of budgets and planning. The feelings come when you are delivering these decisions to real people.&lt;/p&gt;
&lt;p&gt;Framework = rules based, and these rules should be statistically relevant. Rules for one-offs will add a lot unnecessary complexity.&lt;/p&gt;
&lt;p&gt;Use a "Parity" principle: keeping people in the same levels or cohorts in a very close (or identical values)&lt;/p&gt;
&lt;h3 id="build-your-framework"&gt;Build your framework&lt;/h3&gt;
&lt;p&gt;Start with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What does the company need? (specific skills? leadership? an upcoming key project/feature?)&lt;/li&gt;
&lt;li&gt;What are the factors you care most about? (tenure? retention? innovation?)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Create your custom spreadsheet with the header columns.&lt;/p&gt;
&lt;p&gt;First pass strategy: ignore the framework for a moment and just write down what feels right for each person. &lt;/p&gt;
&lt;p&gt;This gut-check baseline helps you see where your instincts diverge from your rules.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;put the basic info in - level, current salary, proposed raise&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;have a running sum at the bottom to see what the total is&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;then remodel and tweak variables&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use a rating system (i.e. performance on a scale of 1 to 5) that maps to percentages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raise&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raise&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raise&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raise&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;of course change those values to whatever you and your organization find best&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Raises should be meaningful to the individual for "a very good performer"&lt;/p&gt;
&lt;p&gt;Create distinct gaps between the "low performers" and "high performers"&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bonuses are for specific effort in a given time period or year, Salary/Compensation is paying someone for ongoing and future responsibilities&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="review"&gt;Review&lt;/h2&gt;
&lt;p&gt;Now that you have your draft...&lt;/p&gt;
&lt;p&gt;Ask HR for data&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;industry comparables (Radford data)&lt;/li&gt;
&lt;li&gt;(anonymized?) other departments/groups in your company&lt;/li&gt;
&lt;li&gt;Historical trends?&lt;/li&gt;
&lt;li&gt;Anomalies or unconscious bias?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ask for feedback from trusted places:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;review with frontline managers (they have to deliver/live with your decisions)&lt;/li&gt;
&lt;li&gt;"what did I miss?"&lt;/li&gt;
&lt;li&gt;"what did you think about ratings/framework?"&lt;/li&gt;
&lt;li&gt;Are you at risk of losing high performers?&lt;/li&gt;
&lt;li&gt;examine individual cases&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="managing-up"&gt;Managing Up&lt;/h3&gt;
&lt;p&gt;You have done your research and created a draft: this output artifact is essential to get real feedback from the people who need to approve it.&lt;/p&gt;
&lt;p&gt;Share it in advance and schedule a meeting. Be open and curious as this is your opportunity to discover more.&lt;/p&gt;
&lt;p&gt;Getting alignment early makes it much easier to subsequently get approval.&lt;/p&gt;
&lt;h1 id="nuances"&gt;Nuances&lt;/h1&gt;
&lt;p&gt;Do look for an opportunity to correct an unconscious/undesired trend - just because someone else made a mistake earlier doesn't let you off the hook&lt;/p&gt;
&lt;p&gt;&lt;em&gt;When you have to fix things, small corrections (sometimes over multiple years) are preferred over one large change that looks like a "shadow promotion"&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Focus on professional performance, Avoid life/family decisions as input: you don't know every invidual person's situation. &lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you skew things and then later learn someone else had "more need" would that be fair? Would you need to ask/pry into every employee's personal and private situations?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Raises as absolute numbers or percentages: most people can understand both so pick one and be consistent.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you use percentages, at larger salaries this can become surprisingly large absolute-number raises&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When someone is getting a raise or promotion, make sure the amount they're receiving "makes a difference".&lt;/p&gt;
&lt;p&gt;Giving someone a 1% raise sometimes feels like the worst of both worlds: you are using up budget and the person may get the sense "we don't value you".&lt;/p&gt;
&lt;p&gt;Either give a larger raise like 3% or explain "HR/company default for middle performers is 1%", here are the things you can do to be a high performer.&lt;/p&gt;
&lt;h2 id="international-and-company-patterns"&gt;International and Company Patterns&lt;/h2&gt;
&lt;p&gt;If you have employees spread internationally it may be very difficult to compare without regard to the local economy. Many innovative organizations have attempted to have a "single global salary" and eventually capitulated to economic realities.&lt;/p&gt;
&lt;p&gt;Your organization may deliberately choose to not be competitive to the market. You will have to indicate that losing people will affect capacity/timelines, and extra costs for recruiting and hiring a replacement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is there an implicit pattern sought or enforced by your company?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The expectation over a large enough distribution of a bell curve or "bottom 10% get 0?"&lt;/p&gt;
&lt;p&gt;&lt;em&gt;it's confronting these types of practices that remind you how important your role is in understanding what the organization says vs does - and how your team are impacted&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="adjusting-to-fit-your-budget"&gt;Adjusting to fit your budget&lt;/h2&gt;
&lt;p&gt;When your compensation proposal doesn't fit your budget, you've got to tweak some things :|&lt;/p&gt;
&lt;p&gt;People who are at the top of a salary band are less likely to feel the a small absolute reduction.&lt;/p&gt;
&lt;p&gt;If you have excess (lucky you!), consider distributing that to the "bottom" earners first as that may have the most relative impact&lt;/p&gt;
&lt;p&gt;Use round numbers: we're human!
- This also means, if you need to round down, do so on the higher end first &lt;/p&gt;
&lt;h1 id="what-if-im-handed-a-finished-compensation-plan"&gt;What if I'm handed a finished compensation plan?&lt;/h1&gt;
&lt;p&gt;Do your homework. If you spot a serious error you can raise it with your manager and potentially prevent an embarrassing or even consequential mistake.&lt;/p&gt;
&lt;p&gt;Showing interest and doing this exercise shows you are preparing to take on more responsibility.&lt;/p&gt;
&lt;h1 id="when-is-it-done"&gt;When is it done?&lt;/h1&gt;
&lt;p&gt;Sleep on your decisions. Review them again with fresh eyes. &lt;/p&gt;
&lt;p&gt;These numbers can affect people's life decisions: a progression in their career, where they live, if they get a new car, where their kids go to school or university, etc. They deserve your best when you're making such critical decisions.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If I got this raise from my manager how would I feel? &lt;/p&gt;
&lt;p&gt;If I was a manager who had to deliver this compensation change would I know what to say and how explain it?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Can the organization deal with the consequences if someone is unhappy and leaves?&lt;/p&gt;
&lt;p&gt;Another round of (hopefully small) tweaks and reviews should go much faster.&lt;/p&gt;
&lt;p&gt;Reality bites: sometimes you do this perfectly and then the organization comes back and asks you to reduce your proposal by 10%.&lt;/p&gt;
&lt;p&gt;No one ever said the work was easy, but someone has to do it, and do it well.&lt;/p&gt;
&lt;h2 id="delivering-the-changes"&gt;Delivering the changes&lt;/h2&gt;
&lt;p&gt;Once it is all approved and you have printed compensation change (or promotion!) letters, practice sharing the news.&lt;/p&gt;
&lt;p&gt;Each person deserves an individual conversation, even if you feel like the change was routine or that it was "a large raise and that should make them happy".&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Research confirms that pay systems affect different people in different ways "Personnel Psychology" from Fulmer et. al.)&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not start with assumptions, every person has unique circumstances and reacts differently&lt;/li&gt;
&lt;li&gt;Do give them the letter with the number and at least a minute to read and process&lt;/li&gt;
&lt;li&gt;Do listen and observe their reaction, and ask them if they have any questions or feedback&lt;/li&gt;
&lt;li&gt;Do not offer any promises to change things, but do take notes and look into concrete concerns&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;sometimes people need a day or week to fully process, be open and prepared for them to bring those questions about a week later&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Part of the reason you put in all the effort to a framework and process is to be prepared to persuade each part of the "Sudoku Puzzle" why the solution you've chosen works.&lt;/p&gt;
&lt;p&gt;And sometimes, when you've done this work with care and delivered it with empathy, even if they're not happy with the result, they will understand that you worked hard on their behalf.&lt;/p&gt;</content><category term="leadership"/><category term="career"/><category term="engineering"/><category term="management"/><category term="budget"/></entry><entry><title>The answer is not the solution</title><link href="https://blog.john-pfeiffer.com/the-answer-is-not-the-solution/" rel="alternate"/><published>2023-01-16T10:00:00-08:00</published><updated>2023-01-16T10:00:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2023-01-16:/the-answer-is-not-the-solution/</id><summary type="html">&lt;p&gt;Look at the number 5: it can have many of representations: IIIII , 10/2 , 05.00 , 5^1&lt;/p&gt;
&lt;p&gt;Those representations are not "wrong", they are all still "5".&lt;/p&gt;
&lt;p&gt;When people argue about a solution, they can both be "right", and yet there is a reason to pick one solution over …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Look at the number 5: it can have many of representations: IIIII , 10/2 , 05.00 , 5^1&lt;/p&gt;
&lt;p&gt;Those representations are not "wrong", they are all still "5".&lt;/p&gt;
&lt;p&gt;When people argue about a solution, they can both be "right", and yet there is a reason to pick one solution over another.&lt;/p&gt;
&lt;p&gt;If you are adding incrementally, 5 becoming 6 and then 7 is a complete rewrite, whereas IIIII I  becomes IIIII II is much easier to append.
&lt;em&gt;(Though it also has a downside of readability at scale ;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you are already starting a problem knowing "10", then "10/2" is a more natural fit.&lt;/p&gt;
&lt;p&gt;When you intend to progress to 05.13 and then 05.99 , clearly decimal has its place.&lt;/p&gt;
&lt;p&gt;One should be aware that assigning the number 5 to a group of stones has no impact on those stones. Our form does not change their content.&lt;/p&gt;
&lt;p&gt;So when you hear of a different solution, rather than assuming someone else is wrong, ask yourself: &lt;em&gt;does this other representation have a different benefit?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Furthermore, when you have cleverly come up with a solution "5", do not mistake that with necessarily changing the world.&lt;/em&gt;&lt;/p&gt;</content><category term="puzzles"/><category term="philosophy"/><category term="architecture"/></entry><entry><title>Front Line Manager or Director - Managing People Managers</title><link href="https://blog.john-pfeiffer.com/front-line-manager-or-director-managing-people-managers/" rel="alternate"/><published>2022-12-03T12:34:00-08:00</published><updated>2022-12-03T12:34:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2022-12-03:/front-line-manager-or-director-managing-people-managers/</id><summary type="html">
&lt;h1 id="titles-mean-nothing"&gt;Titles mean nothing&lt;/h1&gt;
&lt;p&gt;People get hung up on titles and status: "Manager"... of what? Does this mean you get to tell people what to do? So a Director directs people?&lt;/p&gt;
&lt;p&gt;The job is always to solve (hard) problems and create value.&lt;/p&gt;
&lt;p&gt;The kernel of truth: Manager and Director are distinct …&lt;/p&gt;</summary><content type="html">
&lt;h1 id="titles-mean-nothing"&gt;Titles mean nothing&lt;/h1&gt;
&lt;p&gt;People get hung up on titles and status: "Manager"... of what? Does this mean you get to tell people what to do? So a Director directs people?&lt;/p&gt;
&lt;p&gt;The job is always to solve (hard) problems and create value.&lt;/p&gt;
&lt;p&gt;The kernel of truth: Manager and Director are distinct in expected outcomes and focus areas. Director is not as simple as Manager++ or VPE--&lt;/p&gt;
&lt;p&gt;A front-line manager's focus is the immediate results and to care about people who do the work.&lt;/p&gt;
&lt;p&gt;A director's focus is often upward and outward: outcomes that matter to the organization and business, teams and systems that accomplish those things.&lt;/p&gt;
&lt;h1 id="managers-are-necessary-overhead"&gt;Managers are necessary overhead&lt;/h1&gt;
&lt;p&gt;Why aren't top sports teams "self-managed"? These top athletes are the best at what they do, so why do they have a coach?&lt;/p&gt;
&lt;p&gt;People want consistently winning teams.&lt;/p&gt;
&lt;p&gt;If the CEO could just directly tell everyone "Do X" and it worked, they would. &lt;/p&gt;
&lt;p&gt;Likewise every Individual Contributor would prefer autonomy and to focus on their work rather than having to "report to someone" and "performance evaluations".&lt;/p&gt;
&lt;p&gt;A manager represents stability for an organization: a continuing business function that has clear ownership. A dependable and reliable part of the larger structure.&lt;/p&gt;
&lt;p&gt;A manager is not "the next rung up the ladder" for an IC but a completely different kind of work, requiring unique skills and responsibilities.&lt;/p&gt;
&lt;p&gt;It is a lateral move into a different discipline like Design or Science where prior skills and experience don't transfer automatically.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Good managers are worth their weight in gold - or even their weight in bitcoin ;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="creating-leverage-is-valuable"&gt;Creating leverage is valuable&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;What does leverage look like as a People Manager?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An engineering IC creates a frequently used software library or widely adopted architectural pattern: "leverage" to multiply their impact on the organization (and beyond).&lt;/p&gt;
&lt;p&gt;A people manager can uniquely scale their effectiveness and influence by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;hiring and retaining great people&lt;/li&gt;
&lt;li&gt;converting chaos into an efficient process&lt;/li&gt;
&lt;li&gt;building a great culture&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Every org has a lot of noise, someone needs to filter for signal and help provide emphasis.&lt;/p&gt;
&lt;h1 id="as-a-great-manager-manage-yourself"&gt;As a great manager - manage yourself&lt;/h1&gt;
&lt;p&gt;As a Great People Manager, you first manage yourself =)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Are you self aware of your emotional state and mindset - how are you showing up and connecting to others?&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Do you bring positive energy to your team and colleagues?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Do you know what the highest priority most impactful things that you can do are? Effectively manage your own time and calendar?&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Have you spent time trying to understand how your manager and company's leadership team communicate? What is their mindset and favored mechanisms of communication?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Professionalism: on time, prepared for meetings and conversations? Take notes and have action items? Own your action items and follow through?&lt;/li&gt;
&lt;li&gt;Do you communicate clearly in writing, verbally, and with images and diagrams?&lt;/li&gt;
&lt;li&gt;Do you understand your own strength and areas for improvement, what kind of personalities complement your working style?&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Are you deliberately investing in improving your skills and opportunities for more experience?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-does-a-manager-even-do"&gt;What does a Manager even do?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The job is often reminding people of things they already know ;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Manager role is indirect and requires patience: long periods of boring (necessary) work punctuated by busy extremes (hiring, performance reviews, promotions, layoffs, receiving resignations)&lt;/p&gt;
&lt;p&gt;System/code deep dives are intellectually draining - but often emotionally "neutral". People are not logical software. A people manager's role is integrating humans into a system, and humans are emotionally taxing.&lt;/p&gt;
&lt;p&gt;And the job involves common sense: request PTO in advance (so people can cover the work), don't show up 20 minutes late for every meeting.&lt;/p&gt;
&lt;h3 id="a-manager-cares-about-results-and-retention"&gt;A manager cares about Results and Retention&lt;/h3&gt;
&lt;p&gt;A people manager has to "show up", especially in critical times, but overall it is a role requiring reliability.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;accountable for success and failure&lt;/li&gt;
&lt;li&gt;clarify priorities (because situational awareness and context!) and unblock&lt;/li&gt;
&lt;li&gt;aligning the right resources to problems&lt;/li&gt;
&lt;li&gt;ensure a good working environment, that people have tools, processes, etc to do their best work&lt;/li&gt;
&lt;li&gt;how people's work advances the organization, and also furthers careers&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Intensity in 1:1s, Presence for group meetings "crowd control"&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://cate.blog/2015/12/23/the-hardest-shortest-lesson-becoming-a-manager/"&gt;https://cate.blog/2015/12/23/the-hardest-shortest-lesson-becoming-a-manager/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manager-tools.com/2015/06/results-and-retention-part-1-hall-fame-guidance"&gt;https://www.manager-tools.com/2015/06/results-and-retention-part-1-hall-fame-guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manager-tools.com/2015/06/results-and-retention-part-2-hall-fame-guidance"&gt;https://www.manager-tools.com/2015/06/results-and-retention-part-2-hall-fame-guidance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="quality-control"&gt;Quality Control&lt;/h3&gt;
&lt;p&gt;Increased responsibility means more accountability. Every time you hire or promote someone you are putting your credibility (as a decision maker) on the line.&lt;/p&gt;
&lt;p&gt;Having a background in a given domain makes it easier to identify when things are going wrong, or performance issues. (For instance, modifying code directly in live production systems is usually a bad idea).&lt;/p&gt;
&lt;p&gt;As an engineering manager, have you ever seen an engineer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;write unit test coverage for logging but not tests if the feature actually works?&lt;/li&gt;
&lt;li&gt;merge code but always forget to deploy?&lt;/li&gt;
&lt;li&gt;have lots of opinions, arguments, and criticisms in meetings but never write code?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A manager observes, actively listens: Is there a pattern? An unvoiced concern or problem?&lt;/p&gt;
&lt;p&gt;Then a manager shares with the right people a distilled summary of what's going on, and what change is needed.&lt;/p&gt;
&lt;p&gt;Trust and credibility is built by turning pieces of information into action and results.&lt;/p&gt;
&lt;h3 id="mindset-of-a-legacy-manager"&gt;Mindset of a "legacy manager"&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;How many people are in my organization, and how do I get more?&lt;/li&gt;
&lt;li&gt;Hard work is measured by who sits the longest at their desk&lt;/li&gt;
&lt;li&gt;Counting the output of widgets (instead of the outcomes)&lt;/li&gt;
&lt;li&gt;Uses biases (especially pre-conceived) and argumentativeness (or "winning")&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;jumping to a conclusion about who is the decisionmaker; the blame game&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;One on ones and alignment are overhead - they get paid, don't they?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Compliments and praise are over-rated, it's their job!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Why should I develop someone's career when they'll just use it to leave? (or "that's HR's job")&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I should send them my random thoughts at any time, day or night - it's their job to be available and figure it out&lt;/li&gt;
&lt;li&gt;Unnecessarily going fast and taking risks - activity theater&lt;/li&gt;
&lt;li&gt;(over) committing when there's any doubt of followthrough&lt;/li&gt;
&lt;li&gt;Stay in my lane, keep my head down, and mouth shut: enforce that for others too&lt;/li&gt;
&lt;li&gt;premature public announcements!&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;spreading gossip, misinformation, or disinformation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;If reading these things makes you uncomfortable, consider reflecting on the ever evolving nature of the knowledge worker&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="a-modern-manager-of-knowledge-workers"&gt;A Modern Manager of Knowledge Workers&lt;/h3&gt;
&lt;p&gt;A modern manager understands the symbiotic nature of talent and an organization, genuinely finds alignment and valuable opportunities (including when someone's career leads them to another role - even outside of the company), and communicates clearly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solving the problems of your "1st Team"&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Managing people and resources is leverage to solve business problems. Your peers - your first team - are telling you what they (and the business) need. If you only do what your manager tells you, just an extension of only your manager's asks... &lt;/p&gt;
&lt;p&gt;then you're not proactive, not aligned to the business priorities, and not a team player with others genuinely trying to solve company problems.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=BjE_mPoZPSg"&gt;https://www.youtube.com/watch?v=BjE_mPoZPSg&lt;/a&gt; "Team #1" by Patrick Lencioni&lt;/p&gt;
&lt;p&gt;A helpful checklist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;do your direct reports know what your quarterly or monthly goal is? How they can contribute to success?&lt;/li&gt;
&lt;li&gt;do you communicate clearly what you expect?&lt;/li&gt;
&lt;li&gt;when did you last give each direct report a compliment in private? in public?&lt;/li&gt;
&lt;li&gt;when have you invested in your direct reports: coached them through something? they learned something new? received a raise or a promotion?&lt;/li&gt;
&lt;li&gt;expansive view of everything they do to make their organization and group successful&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Litmus test questions about trust:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;do your direct reports feel safe enough and are you curious enough, to learn the names of their loved ones, including pets?&lt;/li&gt;
&lt;li&gt;do your direct reports feel safe enough to bring you problems and give you feedback? &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Recognize people for who they want to be, build trust every day&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prioritizing career development over business needs&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you are prioritizing your own career development or more charitably your direct report's career progressions at the expense of the business outcomes, then you are clearly in a "local maxima".&lt;/p&gt;
&lt;p&gt;Aligning what your direct reports or you need to develop along with business priorities is good management.&lt;/p&gt;
&lt;p&gt;Don't mistake balance for equality...&lt;/p&gt;
&lt;p&gt;Thought experiment: if every manager focused primarily on their direct reports' career development - giving them opportunities to fail and learn and explore - while neglecting what the business needs... and the business fails. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Then nobody has a career to develop!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Challenging Moments and Hard Decisions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If the business is failing then saving the business - which is everybody's job, may be more important than whether your direct report gets a promotion or retained.&lt;/p&gt;
&lt;p&gt;Avoiding needless attrition is common sense, but in a crisis, priorities compress and trade-offs get explicit.&lt;/p&gt;
&lt;h1 id="is-a-director-just-manager-or-vpe-"&gt;Is a Director just Manager++ or VPE--?&lt;/h1&gt;
&lt;p&gt;Director as Manager++: you're a "super manager" who happens to have managers reporting to you. This works when the business is stable and the primary value is execution consistency. It fails spectacularly when the organization needs strategic pivots or cross-functional coordination.&lt;/p&gt;
&lt;p&gt;Director as VPE--: treated as a junior executive, expected to think about business problems, tradeoffs, and org design. This works when the company is scaling or transforming. It fails when you neglect the humans in your organization because you're too busy in leadership meetings.&lt;/p&gt;
&lt;p&gt;As a director, the extra layer of indirection between "leadership" and "front line managers" changes everything.&lt;/p&gt;
&lt;p&gt;Director reality: all the problems are now your problems. Bubbling up from the front lines, and trickling (or crashing ;) down from above.&lt;/p&gt;
&lt;p&gt;Understand the value stream from initial ideas and stakeholders through design and requirements build development, testing deployment, and monitoring.&lt;/p&gt;
&lt;p&gt;Have metrics (or a dashboard) doesn't have to be thorough, just enough to give you a warning signal to investigate.&lt;/p&gt;
&lt;h2 id="managing-a-manager"&gt;Managing a Manager&lt;/h2&gt;
&lt;p&gt;Everybody has a first time they became a manager (and the role is already hard!), a great leader helps people nagivate the change.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hopefully you've learned to become a great people manager before becoming a "manager of managers" - it's never too late!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And just like a Manager is not effective trying to do every one of their direct report's jobs, someone Managing a Manager has to focus on teaching about people management (not taking over or micro-managing).&lt;/p&gt;
&lt;p&gt;As a concrete example, when hearing one of my managers bring up not feeling connected to a new direct report, we discussed that there actually many ways to have a one-on-one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the direct report has their own agenda&lt;/li&gt;
&lt;li&gt;John offers a "choose your own adventure" of topics (e.g. how can I help unblock something? larger context in the org or business? career conversation?)&lt;/li&gt;
&lt;li&gt;the direct report drives with just a stream of consciousness - your job is to actively listen and spot trends and anomalies&lt;/li&gt;
&lt;li&gt;the manager drives (when very comfortable with their manager, likes someone else exposing them to new topics/ideas)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Seek first to understand&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Be curious about their perspective: value and opportunities they see, challenges they experience, approaches they've tried&lt;/li&gt;
&lt;li&gt;help them construct their own individualized plan for success&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a first time front-line manager the sub-graph of the organization is small: often focused "downward" to just their direct reports.&lt;/p&gt;
&lt;p&gt;Teach and encourage (even to the point of discomfort) people managers to navigate peer relationships and "managing up":&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;invest time and build relationships before you need them&lt;/li&gt;
&lt;li&gt;understand other disciplines and roles, what do they need to succeed?&lt;/li&gt;
&lt;li&gt;ensure you listen first, ask and follow through on supporting them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;An existential lesson&lt;/strong&gt; is that Managers create a predictable sub-system.&lt;/p&gt;
&lt;p&gt;Even Managers with innate people relationship talent have to learn to become systematic, have frameworks, and regular feedback (even difficult conversations) to keep things on track.&lt;/p&gt;
&lt;h2 id="what-directors-must-learn-that-managers-can-ignore"&gt;What Directors Must Learn (That Managers Can Ignore)&lt;/h2&gt;
&lt;p&gt;A manager asks "What should my team work on?"&lt;/p&gt;
&lt;p&gt;A director asks "What can only I do, what should I delegate, and what can wait?"&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Be explicit in what level of delegation you are actually expecting: &lt;a href="https://www.jillwetzler.com/resources/delegation"&gt;https://www.jillwetzler.com/resources/delegation&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Know what the org/business needs from your domain.&lt;/strong&gt; A manager can get away with "my team ships good code." A director cannot stop there. You need to understand revenue, runway, competitive positioning, and which parts of your technical portfolio directly impact those things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Proactively share your group's status and health.&lt;/strong&gt; Your manager (VP, CTO, whoever) has limited attention. They need a signal, not a firehose of details. Learn to communicate "we're on track" or "we're at risk because X" in 30 seconds or less.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Improve your group's productivity.&lt;/strong&gt; Not activity, Outcomes! This requires understanding what outcomes matter (see above) and then relentlessly removing obstacles to those outcomes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Efficiency and cost.&lt;/strong&gt; At the manager level, headcount is something that happens to you. At the director level, you're accountable for spend. Every engineer-month is real money invested, are you getting a return?&lt;/p&gt;
&lt;h2 id="engineering-leadership-is-systems-thinking-about-people"&gt;Engineering Leadership is Systems Thinking About People&lt;/h2&gt;
&lt;p&gt;Here's a mental model to consider:&lt;/p&gt;
&lt;p&gt;A great senior engineer knows which service to modify, which file to open, which lines of code to change to have the most impact on the system.&lt;/p&gt;
&lt;p&gt;A director of engineering knows which &lt;em&gt;people&lt;/em&gt; to talk to, what to &lt;em&gt;say&lt;/em&gt; to them, and how to &lt;em&gt;sequence&lt;/em&gt; those conversations to have the most impact on the organization.&lt;/p&gt;
&lt;p&gt;What is the goal of the system, the weaknesses and greatest opportunities?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;growth and transformation&lt;/li&gt;
&lt;li&gt;succession planning&lt;/li&gt;
&lt;li&gt;personality alchemy, blending skills and experiences&lt;/li&gt;
&lt;li&gt;technological investment: balancing innovation and legacy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Directors think in "teams", not "individuals"...&lt;/p&gt;
&lt;p&gt;A manager thinks about "Retention and Results", a Director has to systemize and scale:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reproducible successes&lt;/li&gt;
&lt;li&gt;manage risk&lt;/li&gt;
&lt;li&gt;determinism with people&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Find the right people who understand the high leverage techniques (technology or organizational), find/create the right org structure to put those people in the places where they can do impactful work with the sense of ownership and accountability.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Get the right people on the bus, in the right seats&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://www.jimcollins.com/concepts/first-who-then-what.html"&gt;https://www.jimcollins.com/concepts/first-who-then-what.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="culture-the-thing-directors-actually-own"&gt;Culture: The Thing Directors Actually Own&lt;/h2&gt;
&lt;p&gt;As a manager you participate in culture, yet at the director level, you &lt;em&gt;shape&lt;/em&gt; it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outputs vs Outcomes:&lt;/strong&gt; are you a metrics and process driven organization, or results and outcome driven?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Engineering best practices:&lt;/strong&gt; are they documented? Enforced? Evolving?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Learning tools:&lt;/strong&gt; books, video courses, internal wikis. Do people have access and do they know they exist?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lunch and learns, hackathons, team and company demos:&lt;/strong&gt; these don't happen by accident, make space for them and protect that space when delivery pressure mounts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fun events:&lt;/strong&gt; connecting people matters. Remote-first makes this harder and no longer optional.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interviewer training:&lt;/strong&gt; being an interviewer is a completely different skill. Do your people know how to do it? Are they calibrated?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ownership:&lt;/strong&gt; testing, "you build it you run it," production responsibility. Is this just a slogan or a regular practice?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Blameless retros and continuous learning:&lt;/strong&gt; do people feel safe raising concerns? Do incidents lead to learning or blame?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Psychological safety:&lt;/strong&gt; "see it, say it." If people can't raise problems, the problems don't go away - they just become surprises.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Embracing difficult conversations and feedback, navigating how to have them even when it's uncomfortable but you need to have a conversation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://rework.withgoogle.com/intl/en/guides/understanding-team-effectiveness"&gt;https://rework.withgoogle.com/intl/en/guides/understanding-team-effectiveness&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The spotlight and microphone: what is your organization celebrating? What is getting rewarded as "high performance" and ensuring the optics match reality - help teams see and participate in the wins&lt;/p&gt;
&lt;h1 id="is-something-missing"&gt;Is something missing?&lt;/h1&gt;
&lt;p&gt;Of course =)&lt;/p&gt;
&lt;p&gt;There's no complete guide because it has to be guided by what your organization needs - even as your organization changes.&lt;/p&gt;
&lt;p&gt;When considering becoming a people manager or a Director, the promotion is not simply "more work", these are truly different challenges and roles that deserve careful and open minded learning.&lt;/p&gt;
&lt;h1 id="appendix-of-checklists"&gt;Appendix of Checklists&lt;/h1&gt;
&lt;h2 id="manager-activities"&gt;Manager Activities&lt;/h2&gt;
&lt;p&gt;Ensuring the following for their team members:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;roadmaps, sprints&lt;/li&gt;
&lt;li&gt;tracking work and bugs and launch checklists&lt;/li&gt;
&lt;li&gt;on-call, runbooks, postmortems&lt;/li&gt;
&lt;li&gt;accountability on projects and deliverables&lt;/li&gt;
&lt;li&gt;team and project retrospectives&lt;/li&gt;
&lt;li&gt;how to identify when team has toil, waste? (not all "work" is equal)&lt;/li&gt;
&lt;li&gt;individual and team recognition&lt;/li&gt;
&lt;li&gt;ownership, alignment and growth&lt;/li&gt;
&lt;li&gt;regularly scheduled 1:1s (including taking notes and documenting action items; sharing those for transparency)&lt;/li&gt;
&lt;li&gt;approving expenses and PTO&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-unseen-work-of-a-director"&gt;The Unseen Work of a Director&lt;/h2&gt;
&lt;p&gt;Things a manager rarely thinks about that become a director's problem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensuring compliance and IC training&lt;/li&gt;
&lt;li&gt;Access permissions and SSO/audit improvements&lt;/li&gt;
&lt;li&gt;Helping with SOC2 or other compliance and external audits&lt;/li&gt;
&lt;li&gt;Vendor contracts and licensing (including open source libraries)&lt;/li&gt;
&lt;li&gt;Tools and reports to analyze/audit vendors and usage&lt;/li&gt;
&lt;li&gt;Frameworks to gather data across many systems - not just deployments and incidents, but interactions with other teams and departments&lt;/li&gt;
&lt;li&gt;Cleanup and deletion of old or extraneous systems&lt;/li&gt;
&lt;li&gt;Approving PTOs and building a culture of communication (and coverage) during vacations&lt;/li&gt;
&lt;li&gt;Training your managers - and other people's managers too (yes, you're responsible for this now)&lt;/li&gt;
&lt;li&gt;Coordinating with other departments on how they achieve their goals and their needs&lt;/li&gt;
&lt;li&gt;Compensation analysis with HR&lt;/li&gt;
&lt;li&gt;Standardization and analysis/improvement of hiring with Recruiting&lt;/li&gt;
&lt;li&gt;Creating and Maintaining the career ladder&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this is glamorous. All of it matters.&lt;/p&gt;
&lt;h2 id="the-middle-manager-playbook"&gt;The Middle Manager Playbook&lt;/h2&gt;
&lt;p&gt;Here's what actually happens when you're the quintessential middle manager:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Receive a Vision or Strategy from above (leadership, maybe a passthrough from the board)&lt;/li&gt;
&lt;li&gt;Ask questions to gain understanding: context, scope, expectations, timelines, risk, feasibility&lt;/li&gt;
&lt;li&gt;Write it up in your own words (refer back to the authoritative source doc)&lt;/li&gt;
&lt;li&gt;Understand dependencies and coordination - go from ideas to a plan&lt;/li&gt;
&lt;li&gt;Get feedback from your "right hand person" and trusted peers - this is where being cross-functional is a necessity&lt;/li&gt;
&lt;li&gt;If necessary, convey that feedback all the way back up to the top&lt;/li&gt;
&lt;li&gt;Write up comms (explain the Why!)&lt;/li&gt;
&lt;li&gt;Ensure there are milestones, clarity on who does what (in what order), who the leads/champions are, where/how to ask questions and raise issues&lt;/li&gt;
&lt;li&gt;Roll it out to your org, capture questions and feedback&lt;/li&gt;
&lt;li&gt;Have scheduled check-ins, keep the project on track, and check in "upward" to understand changes in priority, urgency, or scope&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It looks obvious written down but it is not obvious when you're in the middle of three conflicting priorities with incomplete information from above and limited capacity below.&lt;/p&gt;</content><category term="leadership"/><category term="career"/><category term="engineering"/><category term="management"/><category term="leadership"/><category term="director"/></entry><entry><title>Monkeys Stealing Spoons - Why managers struggle and what to do about it</title><link href="https://blog.john-pfeiffer.com/monkeys-stealing-spoons-why-managers-struggle-and-what-to-do-about-it/" rel="alternate"/><published>2021-12-29T12:34:00-08:00</published><updated>2021-12-29T12:34:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2021-12-29:/monkeys-stealing-spoons-why-managers-struggle-and-what-to-do-about-it/</id><summary type="html">
&lt;p&gt;Managers run out of time and emotional energy. Imagine a monkey on your back, stealing your spoons - that's what's happening!&lt;/p&gt;
&lt;p&gt;The solution = leading with empathy + coaching + ownership clarity&lt;/p&gt;
&lt;p&gt;Enduring, high-performing teams aren't built by heroes carrying loads of monkeys, they're built by consistent, sustainable management practices anyone can learn.&lt;/p&gt;
&lt;h1 id="why-do-you-have-a-monkey-on-your-back"&gt;Why …&lt;/h1&gt;</summary><content type="html">
&lt;p&gt;Managers run out of time and emotional energy. Imagine a monkey on your back, stealing your spoons - that's what's happening!&lt;/p&gt;
&lt;p&gt;The solution = leading with empathy + coaching + ownership clarity&lt;/p&gt;
&lt;p&gt;Enduring, high-performing teams aren't built by heroes carrying loads of monkeys, they're built by consistent, sustainable management practices anyone can learn.&lt;/p&gt;
&lt;h1 id="why-do-you-have-a-monkey-on-your-back"&gt;Why do you have a monkey on your back?&lt;/h1&gt;
&lt;p&gt;A metaphor from the classic 1974 Harvard Business Review article "Management Time: Who's Got the Monkey?"&lt;/p&gt;
&lt;p&gt;Time is an irreplaceable resource, and every problem is a monkey that takes your time. &lt;/p&gt;
&lt;p&gt;You might be familiar with the experience: someone sends you a message or brings something up in a meeting "I'm blocked, I don't know how to do this" - and the problem/monkey "jumps from their back onto yours".&lt;/p&gt;
&lt;p&gt;Each time you accept the monkey, you've agreed to carry it. You now own the next action.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hbr.org/1999/11/management-time-whos-got-the-monkey"&gt;https://hbr.org/1999/11/management-time-whos-got-the-monkey&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://archive.is/BItWt"&gt;https://archive.is/BItWt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="management-is-results-and-retention"&gt;Management is Results and Retention&lt;/h1&gt;
&lt;p&gt;"Manager Tools" frames the manager's job as "results and retention".&lt;/p&gt;
&lt;p&gt;The organization wants deliverables within a timeframe, but people are not robots. Managing people means managing emotions too.&lt;/p&gt;
&lt;p&gt;Requests range from "I wanted a seat near the window" to "I'm going to leave this place if I have to work with X again".&lt;/p&gt;
&lt;p&gt;Problems may not be inherently stressful. Yet how we feel about something can be entirely different from what it may take to complete it.&lt;/p&gt;
&lt;p&gt;Your job is to bridge the gap. And that takes energy aka "Spoons"...&lt;/p&gt;
&lt;h2 id="i-just-cant-get-started"&gt;I (just) can't get started&lt;/h2&gt;
&lt;p&gt;"Spoon theory" originated as a way to describe the capacity available to people. We start the day with a finite number of spoons, and someone with chronic illness has "spoons" taken by their illness before they even start their day.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Spoon_theory"&gt;https://en.wikipedia.org/wiki/Spoon_theory&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Every task, conversation, and decision costs spoons. When they're gone, they're gone.&lt;/p&gt;
&lt;p&gt;When you're low on spoons, activation energy feels insurmountable.&lt;/p&gt;
&lt;h2 id="manage-your-spoons-first"&gt;Manage your spoons first&lt;/h2&gt;
&lt;p&gt;Before you can help anyone else, check your own reserves. Do you have enough spoons today to deal with what might come up?&lt;/p&gt;
&lt;p&gt;This is not selfish. You cannot pour from an empty cup. If you're already depleted, you won't have the patience for the small stuff, let alone the capacity for a crisis.&lt;/p&gt;
&lt;p&gt;Sometimes the right answer when asked something is "Let me think about it and get back to you".&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(I'll have another blog post about time management and a "calendar energy audit")&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="reading-other-peoples-spoons"&gt;Reading other people's spoons&lt;/h2&gt;
&lt;p&gt;Lead with empathy: do they have any spoons left today?&lt;/p&gt;
&lt;p&gt;Have you built enough trust that your 1:1s are places where people can be vulnerable?&lt;/p&gt;
&lt;p&gt;Where they can tell you "I'm not really up for this today" - and you actually listen?&lt;/p&gt;
&lt;p&gt;One of the worst 1:1s I ever had was where I asked how someone was doing and they told me they weren't up for a meeting, and for some reason we kept talking. &lt;/p&gt;
&lt;p&gt;Five minutes later they were over-sharing work anxiety, in tears. We agreed to end the meeting and mutually forget everything that was said.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My mistake: I didn't truly listen.&lt;/strong&gt; I didn't realize that given my role power, they may have thought we had to keep talking.&lt;/p&gt;
&lt;p&gt;The lesson: when someone signals "low on spoons", believe them. Don't push through just because the calendar says you have a meeting.&lt;/p&gt;
&lt;p&gt;Now when I hear someone say they're not feeling well I offer to reschedule or focus on 1 urgent thing and end early.&lt;/p&gt;
&lt;h2 id="give-a-spoon"&gt;Give a spoon&lt;/h2&gt;
&lt;p&gt;Sometimes you can give a spoon: with good listening skills and the ability to reframe a situation, you can sometimes actually restore someone's capacity.&lt;/p&gt;
&lt;p&gt;"My memory was that you let me know your highest priority was sitting next to Y, and they got the window seat. They're really happy there, and I'm hoping you're going to be a great combo."&lt;/p&gt;
&lt;p&gt;Helping someone see a situation in a positive light, or not in a zero sum way, is one of the highest-leverage things a manager can do. It costs you relatively little and can shift their entire day or even career.&lt;/p&gt;
&lt;h2 id="protect-the-team-not-just-the-individual"&gt;Protect the team, not just the individual&lt;/h2&gt;
&lt;p&gt;When you notice dysfunction, especially if you care deeply about people, you may be tempted to endure a situation "a little while longer".&lt;/p&gt;
&lt;p&gt;Look back at your 1:1 notes: how many times, and how many people, have brought up the same issue?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;That’s spoons being subtracted from everyone: yourself, them, and the team.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Your job isn't only to support individuals. It's also to protect and support the team. &lt;/p&gt;
&lt;p&gt;Sometimes everyone benefits from addressing the dysfunction directly instead of absorbing it privately.&lt;/p&gt;
&lt;p&gt;I had a manager in my team who knew a direct report wasn't carrying their weight. It was even more difficult as they'd previously been peer-level colleagues.&lt;/p&gt;
&lt;p&gt;Once we'd gotten through reviewing the data objectively, and a coaching discussion, it was the realization his other direct reports had been indirectly bringing the issue up that became the catalyst for change.&lt;/p&gt;
&lt;p&gt;Once the situation resolved the manager and team were not surprised by the outcome and became healthier and stronger.&lt;/p&gt;
&lt;h1 id="helping-on-a-task-versus-helping-growth"&gt;Helping on a task versus Helping Growth&lt;/h1&gt;
&lt;p&gt;A classic first-time manager mistake: taking on your direct reports' work as "helping." You use your IC skills instead of your people management skills. It feels productive because you're good at the work.&lt;/p&gt;
&lt;p&gt;But you're not helping, you're enabling dependency and stealing their growth opportunity. &lt;/p&gt;
&lt;p&gt;Worse: you create an incentive structure where being "good at fixing things" means everyone brings you their things to fix. More problems, more monkeys.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://blog.calm.com/blog/hero-complex&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I can distinctly remember the one-on-one where my manager once told me "You're working harder than anyone in your team. As a manager, that may not actually be a good thing."&lt;/p&gt;
&lt;p&gt;I also remember doing quite a bit of work to help prepare someone's promotion packet. When the promotion didn't happen, it hit me that I'd starting carrying their agency and career growth for them.&lt;/p&gt;
&lt;p&gt;People need to build their own problem-solving capacity and self-advocacy - with support.&lt;/p&gt;
&lt;p&gt;And every monkey you carry costs you spoons.&lt;/p&gt;
&lt;h2 id="coaching"&gt;Coaching&lt;/h2&gt;
&lt;p&gt;Coaching is even more abstract than delegating. You're not doing the work. You're not even telling them how to do it. You're asking questions that help them figure out how they want to do it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This order helps clarify motivation/importance before "What/How"&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;"What makes this important to work on?"&lt;/li&gt;
&lt;li&gt;"What does a successful outcome look like?"&lt;/li&gt;
&lt;li&gt;"What have you already tried?"&lt;/li&gt;
&lt;li&gt;"What do you think is blocking you? What options are you considering?"&lt;/li&gt;
&lt;li&gt;"What would you do if I weren't here?"&lt;/li&gt;
&lt;li&gt;"What will you try next?"&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;(That last part makes it clear - the monkey stays with them)&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Measure a leader by what their team does when their leader's absent&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="delegation"&gt;Delegation&lt;/h2&gt;
&lt;p&gt;Specifically for Delegation, rather than seeing it as "all or nothing", Jill Wetzler synthesized a useful framework from Alan Chapman and Jurgen Appelo's work &lt;em&gt;which I paraphrased...&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Level 1 - Tell: I make the decision. I also tell you what to do and how&lt;/li&gt;
&lt;li&gt;Level 2 - Prep: You do the research, I make the decision (I should explain my reasoning)&lt;/li&gt;
&lt;li&gt;Level 3 - Consult: I ask for your input and recommendation first, then we decide together&lt;/li&gt;
&lt;li&gt;Level 4 - Needs Approval: Propose your plan and action, wait for my approval to continue.&lt;/li&gt;
&lt;li&gt;Level 5 - Expect Feedback: You make the decision and execute but keep me updated, I may have feedback or objections&lt;/li&gt;
&lt;li&gt;Level 6 - Informed: You own this - let me know how it goes&lt;/li&gt;
&lt;li&gt;Level 7 - Escalate (if needed): I don't need to know the details but if something goes wrong seek help&lt;/li&gt;
&lt;li&gt;Level 8 - Autonomous: You got this!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.jillwetzler.com/resources/delegation"&gt;https://www.jillwetzler.com/resources/delegation&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;An important nuance: someone who's great at one thing might be new to a domain or team or project, adjust your level conservatively and accordingly&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="concretely-for-software-engineers"&gt;Concretely for software engineers&lt;/h3&gt;
&lt;p&gt;Higher levels of delegation means more autonomy; more experienced folks should be able to take on complex things with high autonomy...&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tell: Giving an intern a fully laid out (small) task with prescriptive How&lt;/li&gt;
&lt;li&gt;Prep: Asking a junior developer to take on a bug fix&lt;/li&gt;
&lt;li&gt;Consult: Having an engineer work on their first architecture/design proposal&lt;/li&gt;
&lt;li&gt;Needs Approval: A remediation for a production incident (note this is "pairing as an ops best practice", and it's similar to "yes we do Pull Requests")&lt;/li&gt;
&lt;li&gt;Expect Feedback: An engineer leading a small project&lt;/li&gt;
&lt;li&gt;Informed: A senior engineer leading a project&lt;/li&gt;
&lt;li&gt;Escalate (if needed): A senior engineer working on a feature (in a domain they know)&lt;/li&gt;
&lt;li&gt;Autonomous: An engineer fixing a small bug (routine, low risk)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="because-ai-and-agents"&gt;Because AI and Agents&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2025 addendum&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Delegating work to AI Agents is becoming common so here are some examples of how I apply this framework to successfully work with agents...&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tell: how to update an unusual core data schema - high risk, large blast radius, difficult-to-recover&lt;/li&gt;
&lt;li&gt;Prep: Picking the architecture and tech stack - high risk, large blast radius, not-verifiable&lt;/li&gt;
&lt;li&gt;Consult: Working on a large feature - medium risk, large blast radius, hard-to-verify result&lt;/li&gt;
&lt;li&gt;Needs Approval: Working on a small feature - some risk, small blast radius, verifiable result&lt;/li&gt;
&lt;li&gt;Expect Feedback: Working on a small bug fix - some risk, small blast radius, easily verifiable result&lt;/li&gt;
&lt;li&gt;Informed: Writing unit tests - low risk&lt;/li&gt;
&lt;li&gt;Escalate (if needed): Updating dependencies - low risk, trivial verification&lt;/li&gt;
&lt;li&gt;Autonomous: Updating documentation (have this as a daily cron job) - very low risk/impact&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="summary"&gt;Summary&lt;/h1&gt;
&lt;p&gt;A manager is great when inspiring and elevating people. Ensure you're in the right frame of mind to be that leader.&lt;/p&gt;
&lt;p&gt;Don't be a hero who accepts all the monkeys; they steal all of your spoons!&lt;/p&gt;
&lt;p&gt;Give people spoons. Coach them to tame their monkeys.&lt;/p&gt;
&lt;p&gt;Over-communicated and mutually understood delegation makes ownership even clearer.&lt;/p&gt;</content><category term="leadership"/><category term="career"/><category term="leadership"/><category term="management"/><category term="manager"/><category term="director"/><category term="hero"/><category term="coaching"/><category term="delegation"/></entry><entry><title>CircleCI for a Pelican static Github site</title><link href="https://blog.john-pfeiffer.com/circleci-for-a-pelican-static-github-site/" rel="alternate"/><published>2021-09-03T21:00:00-07:00</published><updated>2021-09-03T21:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2021-09-03:/circleci-for-a-pelican-static-github-site/</id><summary type="html">
&lt;p&gt;When I realized my previous CI/CD vendor had finally broken something &lt;em&gt;(after 7 years travis.org incompatibly became travis.com)&lt;/em&gt; I decided out with the old and in with the new. Also, Python2 was deprecated so onto Python3!&lt;/p&gt;
&lt;p&gt;So this article is a non-comprehensive reprise of How to Setup …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;When I realized my previous CI/CD vendor had finally broken something &lt;em&gt;(after 7 years travis.org incompatibly became travis.com)&lt;/em&gt; I decided out with the old and in with the new. Also, Python2 was deprecated so onto Python3!&lt;/p&gt;
&lt;p&gt;So this article is a non-comprehensive reprise of How to Setup a Static Site with Github Pages...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/how-to-set-up-a-pelican-static-blog-site/"&gt;https://blog.john-pfeiffer.com/how-to-set-up-a-pelican-static-blog-site/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/static-site-pelican-blog-with-github-pages-and-travis-ci/"&gt;https://blog.john-pfeiffer.com/static-site-pelican-blog-with-github-pages-and-travis-ci/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Luckily I already had dabbled in CircleCI so the fundamentals were an easy copy paste: &lt;a href="https://blog.john-pfeiffer.com/using-circleci-as-continuous-integration-and-continuous-deployment/"&gt;https://blog.john-pfeiffer.com/using-circleci-as-continuous-integration-and-continuous-deployment/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Roughly we will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;have python&lt;/li&gt;
&lt;li&gt;install dependencies (pelican)&lt;/li&gt;
&lt;li&gt;(optional) install plugins&lt;/li&gt;
&lt;li&gt;get blog source markdown &lt;em&gt;(ideally from version control like github)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;run build commands &lt;em&gt;(to convert markdown to html)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;push to a second github repository&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="a-new-pelican-dev-environment"&gt;A new Pelican Dev Environment&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;unfortunately getting Docker setup is outside the scope of this tutorial&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Just a practice run so you get a feel for it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;-it&lt;span class="w"&gt; &lt;/span&gt;cimg/python:3.8&lt;span class="w"&gt; &lt;/span&gt;/bin/bash
python&lt;span class="w"&gt; &lt;/span&gt;--version
python&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pelican[markdown]"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;beautifulsoup4
&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;an ephemeral docker container with interactive bash shell, cimg is the CircleCI optimized docker image, https://hub.docker.com/u/cimg versus https://hub.docker.com/r/circleci/python/&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Installing Pelican is documented: &lt;a href="https://docs.getpelican.com/en/latest/"&gt;https://docs.getpelican.com/en/latest/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="using-pelican-quickstart-for-a-skeleton"&gt;Using pelican-quickstart for a skeleton&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;assuming you are still in your docker container shell&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;PYVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;/home/circleci/.pyenv/versions&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.8&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/circleci/.pyenv/versions/&lt;/span&gt;&lt;span class="nv"&gt;$PYVER&lt;/span&gt;&lt;span class="s2"&gt;/bin/"&lt;/span&gt;
./pelican&lt;span class="w"&gt; &lt;/span&gt;--version
mkdir&lt;span class="w"&gt; &lt;/span&gt;yoursite
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;yoursite
../pelican-quickstart
&lt;span class="w"&gt;  &lt;/span&gt;Welcome&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;pelican-quickstart&lt;span class="w"&gt; &lt;/span&gt;v4.6.0.
&lt;span class="w"&gt;  &lt;/span&gt;This&lt;span class="w"&gt; &lt;/span&gt;script&lt;span class="w"&gt; &lt;/span&gt;will&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;Pelican-based&lt;span class="w"&gt; &lt;/span&gt;website.
&lt;span class="w"&gt;  &lt;/span&gt;...&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;accept&lt;span class="w"&gt; &lt;/span&gt;all&lt;span class="w"&gt; &lt;/span&gt;defaults&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;...
&lt;span class="w"&gt;  &lt;/span&gt;Done.&lt;span class="w"&gt; &lt;/span&gt;Your&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;project&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;available&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;/home/circleci/.pyenv/versions/3.8.11/bin/yoursite
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;A little directory dancing in the container to get to the pip installed pelican binary&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You still need an article with a minimum of content...&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vim yoursite/content/my-article.md&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Title: My Article
Date: 2021-09-09 22:22
Category: Technology

Hello world.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="generate-html-from-markdown"&gt;Generate html from markdown&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;./pelican yoursite/content/&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;creates an "output" directory with the HTML and all the index and other pages also updated&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;my-article.html
archives.html
authors.html
categories.html
category
feeds
index.html
tags.html
theme
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="docker-volume-with-your-pre-existing-blog-content"&gt;Docker volume with your pre-existing blog content&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--volume&lt;span class="w"&gt; &lt;/span&gt;/home/ubuntu/blogsource/:/home/circleci/blogsource&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;-it&lt;span class="w"&gt; &lt;/span&gt;cimg/python:3.8&lt;span class="w"&gt; &lt;/span&gt;/bin/bash
mkdir&lt;span class="w"&gt; &lt;/span&gt;/home/circleci/OUT
&lt;span class="nv"&gt;PYVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;/home/circleci/.pyenv/versions&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.8&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/circleci/.pyenv/versions/&lt;/span&gt;&lt;span class="nv"&gt;$PYVER&lt;/span&gt;&lt;span class="s2"&gt;/bin/"&lt;/span&gt;
./pelican&lt;span class="w"&gt; &lt;/span&gt;/home/circleci/blogsource/pelican-project/content&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;/home/circleci/OUT&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;/home/circleci/blogsource/pelican-project/publishconf.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Docker will mount your local directory with pelican project markdown mapped to "/home/circleci/blogsource" in the docker container&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;If you do not specify a new "output directory" then pelican may get confused if there is already content in "pelican-project/output"&lt;/em&gt;&lt;/p&gt;
&lt;h1 id="linking-github-to-circleci"&gt;Linking Github to CircleCI&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;assuming you have your blog markdown in a Github repository and logged into CircleCI and authorized it for access to your Github repos&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Click on the CircleCI button for your source code: "Setup a Project"&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use CircleCI's template for a configuration (a python project)&lt;/li&gt;
&lt;li&gt;customize it: &lt;a href="https://circleci.com/docs/2.0/executor-types/"&gt;https://circleci.com/docs/2.0/executor-types/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;trial and error to get various commands in the docker container right (use the Dev environment above)&lt;/li&gt;
&lt;li&gt;There is a live-config-editor "Edit Config"&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;save and merge (.circleci/config.yml)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now your source code (markdown) for your (pelican) blog should have a CircleCI configuration like the following:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;.circleci/config.yml&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;2.1&lt;/span&gt;
&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;resource_class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;small&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;cimg/python:3.8&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;checkout&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Install Pelican and Build Content&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;python -m pip install "pelican[markdown]" beautifulsoup4&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;pip freeze | grep pelican&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;cd ..&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;pwd&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;ls -ahl /home/circleci/project&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;find . -type f -iname pelican&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;PYVER=$(ls /home/circleci/.pyenv/versions | grep 3.8)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;cd ".pyenv/versions/$PYVER/bin/"&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;./pelican --version&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;mkdir -p /home/circleci/OUT&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;./pelican /home/circleci/project/content -o /home/circleci/OUT -s /home/circleci/project/publishconf.py&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;ssh-add -D&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;add_ssh_keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nt"&gt;fingerprints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;                          &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"4e:c1:a6:83:...:cc"&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Publish to GitHub Static Site&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;cd ..&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;ls -ahl&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;git clone git@github.com:johnpfeiffer/johnpfeiffer.github.io.git&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;cd johnpfeiffer.github.io&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;git config user.email "me@john-pfeiffer.com"&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;git config user.name "John Pfeiffer CircleCI"&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;git checkout master&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;cp -a /home/circleci/OUT/* .&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;git status&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;git add --all .&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;git commit --allow-empty -m "CircleCI publishing $CIRCLE_BUILD_NUM from sha $CIRCLE_SHA1"&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;ls -ahl ~/.ssh/&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="no"&gt;GIT_SSH_COMMAND='ssh -v -i ~/.ssh/id_rsa_4ec1a683...cc' git push origin master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Everything from add_ssh_keys and below should only be added once you have completed the SSH Key steps below&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Removing all SSH keys from the CircleCI agent is because we're done checking out this repo and need to not confuse Git later&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id="pushing-to-a-github-page-repository"&gt;Pushing to a Github Page repository&lt;/h1&gt;
&lt;p&gt;The target for all of this has been your Github static page: &lt;a href="https://pages.github.com/"&gt;https://pages.github.com/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="create-a-new-ssh-key-dedicated-to-only-this-purpose"&gt;Create a new SSH key dedicated to only this purpose&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ssh-keygen&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;rsa&lt;span class="w"&gt; &lt;/span&gt;-b&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4096&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-C&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CircleCI Deploy Key with Write Access"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;/tmp/cikey
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that this key is just for CircleCI for just this one repo&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Navigate to the "Deploy keys" section of the Settings of the Repo in github that will receive the pushes
e.g https://github.com/johnpfeiffer/johnpfeiffer.github.io/settings/keys&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;click on "Add deploy key"&lt;/li&gt;
&lt;li&gt;Paste in the cikey.pub file contents&lt;/li&gt;
&lt;li&gt;Ensure the "Allow write access" checkbox is checked&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In CircleCI:
Go to your source project (under your username) and choose the "Project Settings" button, then subsection SSH Keys
e.g. https://app.circleci.com/settings/project/github/johnpfeiffer/source.johnpfeiffer.github.io/ssh&lt;/p&gt;
&lt;p&gt;Copy the private "cikey" (which starts with "-----BEGIN RSA PRIVATE KEY-----") into your buffer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do not share or paste your private key anywhere else than CircleCI&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Add an Additional SSH Key "Add SSH Key"
Hostname: githubstaticpage
Private Key: -----BEGIN RSA PRIVATE KEY-----...&lt;/p&gt;
&lt;p&gt;Now copy the "Fingerprint" of the key e.g. something like 4e:c1:...
This is what goes in your circleCI config&lt;/p&gt;
&lt;h2 id="circleci-config-explained"&gt;CircleCI config explained&lt;/h2&gt;
&lt;p&gt;This portion is your CircleCI agent manually checking out the actual "Pages" HTML repository:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;            git clone git@github.com:johnpfeiffer/johnpfeiffer.github.io.git
            cd johnpfeiffer.github.io
            git config user.email "me@john-pfeiffer.com"
            git config user.name "John Pfeiffer CircleCI"
            git checkout master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It is important to copy the new HTML content in...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cp&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;/home/circleci/OUT/*&lt;span class="w"&gt; &lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;technical debt: should use the rsync command to actually reflect removed content too&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here is where the previously created SSH Key and fingerprint are explicitly used to specify to git to use that ssh key to push to github...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;        GIT_SSH_COMMAND='ssh -v -i ~/.ssh/id_rsa_4ec1a683...cc' git push origin master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;ssh verbose shows you which Identity/Key is being used to deploy/push to Github&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;References&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://circleci.com/docs/2.0/add-ssh-key/"&gt;https://circleci.com/docs/2.0/add-ssh-key/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.jdblischak.com/posts/circleci-ssh/"&gt;https://blog.jdblischak.com/posts/circleci-ssh/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discuss.circleci.com/t/multiple-deploy-keys-for-github/25658/8"&gt;https://discuss.circleci.com/t/multiple-deploy-keys-for-github/25658/8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discuss.circleci.com/t/cloning-another-private-repo-in-the-build/25505/3"&gt;https://discuss.circleci.com/t/cloning-another-private-repo-in-the-build/25505/3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="pelican-plugins"&gt;Pelican Plugins&lt;/h1&gt;
&lt;p&gt;My site still uses the old plugins which I "vendored" into the actual repo for simplicity:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/johnpfeiffer/source.johnpfeiffer.github.io/tree/master/plugins"&gt;https://github.com/johnpfeiffer/source.johnpfeiffer.github.io/tree/master/plugins&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/getpelican/pelican-plugins"&gt;https://github.com/getpelican/pelican-plugins&lt;/a&gt;&lt;blockquote&gt;
&lt;p&gt;10 MB of legacy plugins at "getpelican" because they still work&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instructions on Pelican Plugins in general (and the new way that I have not yet adopted)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.getpelican.com/en/latest/plugins.html"&gt;https://docs.getpelican.com/en/latest/plugins.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thankfully this person documented how to use Pelican4.6 with MARKDOWN for the built in Table of Contents (TOC):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloudbytes.dev/articles/add-a-table-of-contents-using-markdown-in-pelican"&gt;https://cloudbytes.dev/articles/add-a-table-of-contents-using-markdown-in-pelican&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="build-CI-CD-devops"/><category term="circleci pelican github"/></entry><entry><title>Using CircleCI as continuous integration and continuous deployment</title><link href="https://blog.john-pfeiffer.com/using-circleci-as-continuous-integration-and-continuous-deployment/" rel="alternate"/><published>2021-04-14T20:43:00-07:00</published><updated>2021-04-14T20:43:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2021-04-14:/using-circleci-as-continuous-integration-and-continuous-deployment/</id><summary type="html">
&lt;p&gt;I avoid providing free advertising for products but often end up writing about how I have leveraged free products. =]&lt;/p&gt;
&lt;p&gt;I have many posts over the years using vmware, openshift, heroku, google app engine, aws elastic beanstalk, ec2, aws lambdas, github, bitbucket, bitbucket pipelines, docker, digital ocean, and many more things …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;I avoid providing free advertising for products but often end up writing about how I have leveraged free products. =]&lt;/p&gt;
&lt;p&gt;I have many posts over the years using vmware, openshift, heroku, google app engine, aws elastic beanstalk, ec2, aws lambdas, github, bitbucket, bitbucket pipelines, docker, digital ocean, and many more things. &lt;em&gt;(all lowercase ;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I wanted to document how I successfully migrated some of my other hobby projects to CircleCI so future me (and anyone else) can more easily replicate the steps because CircleCI has a free tier for CI/CD (continuous integration and continuous deployment).&lt;/p&gt;
&lt;p&gt;I make no guarantee that CircleCI will continue to be free in the future (but if stops being free I will likely write another blog post about how to use a different service ;)&lt;/p&gt;
&lt;h2 id="why-i-chose-circleci"&gt;Why I chose CircleCI&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Free (even more free for open source projects)&lt;/li&gt;
&lt;li&gt;Straightforward documentation with good examples&lt;/li&gt;
&lt;li&gt;Integrated with github (and bitbucket)&lt;/li&gt;
&lt;li&gt;It just works (relatively quick execution and feedback loop)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="circleci-build-terminology"&gt;CircleCI Build Terminology&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;Project&lt;/strong&gt; maps to a code repository&lt;/li&gt;
&lt;li&gt;You configure each Project to do certain things when a new commit occurs.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;pipeline&lt;/strong&gt; are all the things that happen when your Project is triggered (i.e. new code or manual re-run).&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;workflow&lt;/strong&gt; is the definition (and execution) of all the jobs in a pipeline; note that jobs can run in parallel.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;job&lt;/strong&gt; is a collection of steps that are going to happen (i.e. checkout code and run a command)&lt;/li&gt;
&lt;li&gt;A job must have an execution environment (i.e. a docker container)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;step&lt;/strong&gt; is doing a single thing (i.e. checkout code)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://circleci.com/docs/2.0/concepts/?section=getting-started"&gt;https://circleci.com/docs/2.0/concepts/?section=getting-started&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="getting-started-by-authorizing-circleci"&gt;Getting Started by Authorizing CircleCI&lt;/h2&gt;
&lt;p&gt;Start with the source code: setup a repository (i.e. in github) , e.g. &lt;a href="https://github.com/johnpfeiffer/stringsmoar"&gt;https://github.com/johnpfeiffer/stringsmoar&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Go to CircleCI's login page and choose to "Sign Up" &lt;a href="https://circleci.com/signup/"&gt;https://circleci.com/signup/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;For the paranoid like me you can choose to only share your public github repos&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Once you use the oauth-like permissions screen that provides your username and password to Github so it can authorize CircleCI to access all of your bits.&lt;/p&gt;
&lt;p&gt;This is the one time you "authorize all the access", everything afterwards are config files that are fine to be in public code repos.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In Github you can review what applications have access to your github account's source code with &lt;a href="https://github.com/settings/applications"&gt;https://github.com/settings/applications&lt;/a&gt;
&lt;strong&gt;"Authorized Oauth Apps"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In CircleCI you can now see what projects you can setup builds, apparently segregated by "organization" &lt;a href="https://app.circleci.com/projects/"&gt;https://app.circleci.com/projects/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="setup-a-project"&gt;Setup a Project&lt;/h2&gt;
&lt;p&gt;Not too surprisingly the UI then displays a list of all of the repos &lt;a href="https://app.circleci.com/projects/project-dashboard/github/johnpfeiffer/"&gt;https://app.circleci.com/projects/project-dashboard/github/johnpfeiffer/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;"Set Up Project" makes sense but for some odd reason the terminology is to "follow a project" when something has been configured by someone else in your organization&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The UI will attempt to helpfully suggest a configuration yaml based on auto-detecting the repository's programming language.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;For Golang CircleCI presumes you are using &lt;strong&gt;go mod&lt;/strong&gt; so I guess I ought to upgrade my old code repos now that there's an official standard&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you choose the pre-generated configuration file CircleCI will attempt to commit and push that new .circleci/config.yml into your repo and then start a Build.&lt;/p&gt;
&lt;p&gt;Instead of the auto-generated configuration you can select &lt;strong&gt;Use Existing Config&lt;/strong&gt; (in which case you should have already uploaded into github remote your preferred CircleCI reference)...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;your-repo/.circleci/config.yml&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;2.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# https://circleci.com/docs/2.0/configuration-reference&lt;/span&gt;
&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;resource_class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;small&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;working_directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;~/repo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# the circleCI default for where code is checked out to in the docker build container&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;cimg/go:1.21.4&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;checkout&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Run unit tests&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;go test -v ./...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="double-checking-your-golang-version"&gt;Double checking your Golang Version&lt;/h3&gt;
&lt;p&gt;The wonderful thing about Docker is the extra transparency. In this case we might want to double check the version of Golang that is being used by the build agent.&lt;/p&gt;
&lt;p&gt;You can download and execute the same environment locally:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;-it&lt;span class="w"&gt; &lt;/span&gt;circleci/golang:1.21.4
go&lt;span class="w"&gt; &lt;/span&gt;version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;go version go1.21.4 linux/amd64&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/r/circleci/golang/"&gt;https://hub.docker.com/r/circleci/golang/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cannot-find-main-module-is-a-common-error-for-older-golang-code-repos"&gt;Cannot find main module is a common error for older golang code repos&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;go: cannot find main module&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This means you have created a golang repo awhile back &lt;em&gt;(Golang 1.15 and older)&lt;/em&gt; but are now using a newer/later Golang binary...&lt;/p&gt;
&lt;p&gt;To resolve the issue run this golang command in the top directory of your source code:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;go mod init&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will create a &lt;strong&gt;go.mod&lt;/strong&gt; file in your repository that allows dependencies to be properly resolved (and &lt;code&gt;go test&lt;/code&gt; which implicitly uses "go mod" to execute successfully)&lt;/p&gt;
&lt;p&gt;Once that go.mod is committed and sent up to the Main branch in your github repo then CircleCI build will detect it and your "go test" step during the build/test steps will stop failing&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org/ref/mod#mod-commands"&gt;https://golang.org/ref/mod#mod-commands&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="tweaks-to-your-circleci-config"&gt;Tweaks to your CircleCI Config&lt;/h2&gt;
&lt;p&gt;After you have successfully run a build then the UI will show you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how long the build took&lt;/li&gt;
&lt;li&gt;what git sha commit kicked off the build&lt;/li&gt;
&lt;li&gt;commit message&lt;/li&gt;
&lt;li&gt;all the steps executed and output, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://app.circleci.com/pipelines/github/johnpfeiffer/stringsmoar/7/workflows/39b3f880-2473-49d7-804d-d1364f08853e/jobs/9"&gt;https://app.circleci.com/pipelines/github/johnpfeiffer/stringsmoar/7/workflows/39b3f880-2473-49d7-804d-d1364f08853e/jobs/9&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="rerun-a-build-in-circleci"&gt;Rerun a build in CircleCI&lt;/h3&gt;
&lt;p&gt;Sometimes it can take a bit to get used to the CircleCI UI, to drill down to a specific build your "breadcrumbs" will look like:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;All Pipelines &amp;gt; your-projectname &amp;gt; branch (main) &amp;gt; workflow &amp;gt; build (4)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In that detailed output UI, to Rerun a build, choose the "Rerun" button from the beginning (or from a failed step)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Flaky tests aka intermittent failures is not resolved by re-running your build/tests all the time ;p&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="specific-project-settings-in-circleci"&gt;Specific Project Settings in CircleCI&lt;/h3&gt;
&lt;p&gt;Deploy Keys (specific to a repo) are a better security practice - read below on how to configure your poject to use them =)&lt;/p&gt;
&lt;p&gt;In the CircleCI UI, for a given Project, the three little dots will allow you to choose how to configure the project&lt;/p&gt;
&lt;p&gt;&lt;a href="https://app.circleci.com/settings/project/github/johnpfeiffer/stringsmoar"&gt;https://app.circleci.com/settings/project/github/johnpfeiffer/stringsmoar&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The one annoying thing is that if you remove your 3rd party access creds in GitHub it's a pain to reconnect CircleCI&lt;/p&gt;
&lt;p&gt;In the CircleCI configuration for a Project you should see a listing of SSH keys
you have to remove (delete) the old "deployment key" there (which means CircleCI can no longer access github)
then choose to re-add a deployment key (if you are signed into CircleCI with GitHub this will automatically generate it)&lt;/p&gt;
&lt;p&gt;Or unfollow the project, and then re-follow the project &lt;em&gt;(which will then have CircleCI use your initial OAuth authorization to generate a new SSH deployment key in GitHub).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Afterward you should see a new SSH key that CircleCI created in Github for this Project &lt;em&gt;(the UI's both show the sha of the key but one is sha256 and the other is not)&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://circleci.com/docs/github-integration/#deploy-keys-and-user-keys"&gt;https://circleci.com/docs/github-integration/#deploy-keys-and-user-keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://discuss.circleci.com/t/solved-permission-denied-publickey/19562"&gt;https://discuss.circleci.com/t/solved-permission-denied-publickey/19562&lt;/a&gt; &lt;em&gt;(someone else had the same problem and documented their solution)&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/managing-deploy-keys#deploy-keys"&gt;https://docs.github.com/en/authentication/connecting-to-github-with-ssh/managing-deploy-keys#deploy-keys&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/johnpfeiffer/stringsmoar/settings/keys"&gt;https://github.com/johnpfeiffer/stringsmoar/settings/keys&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="picking-the-size-of-your-build-executor"&gt;Picking the size of your build executor&lt;/h3&gt;
&lt;p&gt;By default CircleCI will choose executor size of "medium", if you want to save (free) credits then for smaller projects use "small" &lt;em&gt;(or conversely if you need more cpu/ram choose a larger size)&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;resource_class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;small&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;circleci/golang:1.16&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Good news: builds usually trigger almost instantly so all the little config tweaks have a super fast feedback loop&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://circleci.com/docs/2.0/executor-types/#available-docker-resource-classes"&gt;https://circleci.com/docs/2.0/executor-types/#available-docker-resource-classes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://circleci.com/docs/2.0/getting-started/?section=getting-started"&gt;https://circleci.com/docs/2.0/getting-started/?section=getting-started&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="outputting-test-coverage-and-artifacts"&gt;Outputting Test Coverage and Artifacts&lt;/h2&gt;
&lt;p&gt;CircleCI has an extra space in the UI to display specific test output or artifacts which makes it easy to see the most common pain points rather than digging through all of the build stages output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;your-repo/.circleci/config.yml&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;2.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# https://circleci.com/docs/2.0/configuration-reference&lt;/span&gt;
&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;resource_class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;small&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;working_directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;~/repo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# this is a circleCI default for where code is checked out to in the docker build container&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;circleci/golang:1.16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# https://hub.docker.com/r/circleci/golang/ , https://hub.docker.com/_/golang?tab=description&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;TEST_RESULTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/tmp/test-results&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;checkout&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Run unit tests&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;go test -v ./...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Run code coverage&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;mkdir -p $TEST_RESULTS&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;go test -coverprofile=c.out&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;go tool cover -html=c.out -o coverage.html&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;mv coverage.html $TEST_RESULTS&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;go test -v ./... | go tool test2json &amp;gt; $TEST_RESULTS/test2json-output.json&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;gotestsum --junitfile $TEST_RESULTS/gotestsum-report.xml&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;store_artifacts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Upload files like code coverage html for later viewing https://circleci.com/docs/2.0/artifacts/&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/tmp/test-results&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;raw-test-output&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;store_test_results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Upload test results for display https://circleci.com/docs/2.0/collect-test-data/&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/tmp/test-results&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Artifacts will be deleted after 30 days but would be output like this: &lt;a href="https://12-123862890-gh.circle-artifacts.com/0/raw-test-output/coverage.html#file0"&gt;https://12-123862890-gh.circle-artifacts.com/0/raw-test-output/coverage.html#file0&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The golang coverage.html as an artifact can be opened by your web browser and highlight in color specifically which code paths are covered by unit tests&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/cover"&gt;https://blog.golang.org/cover&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://circleci.com/docs/2.0/artifacts/"&gt;https://circleci.com/docs/2.0/artifacts/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://circleci.com/docs/2.0/language-go/"&gt;https://circleci.com/docs/2.0/language-go/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/golang-testing-benchmark-profiling-subtests-fuzz-testing/"&gt;https://blog.john-pfeiffer.com/golang-testing-benchmark-profiling-subtests-fuzz-testing/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;the CircleCI golang docker container has the opensource helper "gotestsum" to generate junit style XML output from tests&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;JUnit XML or Cucumber JSON test metadata files&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;From here on out you should hopefully have only Green Builds!&lt;/p&gt;
&lt;p&gt;TODO: an article about how to do continuous deployment &lt;em&gt;(maybe CDK and AWS?)&lt;/em&gt;&lt;/p&gt;</content><category term="build-CI-CD-devops"/><category term="ci"/><category term="circleci"/><category term="continuous integration"/><category term="continuous deployment"/><category term="github"/><category term="golang"/><category term="go"/><category term="go mod"/></entry><entry><title>Bitcoin is a bad business model and when to invest in your Cost Center</title><link href="https://blog.john-pfeiffer.com/bitcoin-is-a-bad-business-model-and-when-to-invest-in-your-cost-center/" rel="alternate"/><published>2021-01-23T08:44:00-08:00</published><updated>2021-01-23T08:44:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2021-01-23:/bitcoin-is-a-bad-business-model-and-when-to-invest-in-your-cost-center/</id><summary type="html">
&lt;h2 id="bitcoin-is-a-bad-business-model"&gt;Bitcoin is a bad business model&lt;/h2&gt;
&lt;p&gt;A good business model creates value. Eventually it will create good cashflow (i.e. more money in your bank account each month).&lt;/p&gt;
&lt;p&gt;While I understand that some may find the concept of an "unowned" virtual currency appealing, creating bitcoin (aka "mining") in and of …&lt;/p&gt;</summary><content type="html">
&lt;h2 id="bitcoin-is-a-bad-business-model"&gt;Bitcoin is a bad business model&lt;/h2&gt;
&lt;p&gt;A good business model creates value. Eventually it will create good cashflow (i.e. more money in your bank account each month).&lt;/p&gt;
&lt;p&gt;While I understand that some may find the concept of an "unowned" virtual currency appealing, creating bitcoin (aka "mining") in and of itself is not differentiated and does not create value.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Buy (or rent) a server, run software with an algorithm, hope that your 1s and 0s are special enough to be "worth something"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Note that the control of the "coins or transactions accepted" is defined by an external entity, so anyone can use the protocol&lt;/em&gt;
&lt;a href="https://en.wikipedia.org/wiki/Satoshi_Nakamoto"&gt;https://en.wikipedia.org/wiki/Satoshi_Nakamoto&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Coinbase (&lt;a href="https://www.bloomberg.com/profile/company/0776164D:US"&gt;https://www.bloomberg.com/profile/company/0776164D:US&lt;/a&gt;) has a business model of creating a platform for speculators to gamble (on many crypto-currencies), but just like selling shovels during a gold rush, they are not in the virtual hills digging, instead they are comfortably collecting a huge pile of gold dust.&lt;/p&gt;
&lt;p&gt;With Bitcoin, there is no "moat" (&lt;a href="https://www.investopedia.com/terms/e/economicmoat.asp"&gt;https://www.investopedia.com/terms/e/economicmoat.asp&lt;/a&gt;); no way to grow your business other than increasing efficiency and reducing costs. It is not inherently more valuable to have more virtual 1s and 0s when there are an infinite number of possible random numbers, nor is it particularly valuable to write your own IOUs that are guaranteed by random other people writing IOUs.&lt;/p&gt;
&lt;p&gt;You cannot add so much efficiency to a Cost Center that it suddenly becomes a business model of its own.&lt;/p&gt;
&lt;h3 id="creating-value"&gt;Creating Value&lt;/h3&gt;
&lt;p&gt;Growing food is a way of creating value: people need food and someone creates it.&lt;/p&gt;
&lt;p&gt;Do people need cryptocurrency? Creating things that people want can be valuable (see Art).&lt;/p&gt;
&lt;p&gt;So Bitcoin is valuable as a way to digitally exchange value, and because other people think it might be more valuable later.&lt;/p&gt;
&lt;p&gt;A good business model should create more value, ideally in a novel and innovative way. Intrinsically then Bitcoin and mining Bitcoin are not creating value but "enabling value".&lt;/p&gt;
&lt;p&gt;The more you create Bitcoin the more you are helping someone else capture value ;)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Without bitcoin how would crime in the digital world have been able to innovate? So maybe create Bitcoin as a good "loss leader" or a "cost center"?&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="cost-center-and-profit-center"&gt;Cost Center and Profit Center&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Like many ideas, it is not the thing, it is just a way to think about things&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The "Profit Center" concept was coined by Peter Drucker in 1945 and it refers to thinking about the role a department or group (or project) plays in a company.&lt;/p&gt;
&lt;p&gt;One part of the company is making something of value, the core of the business that you are selling.&lt;/p&gt;
&lt;p&gt;As an example, "creating and selling food" is the profit center of some example business.&lt;/p&gt;
&lt;p&gt;So the management of the people who are "creating and selling food" is a Cost Center. (Yup, your manager is costing the business money!).&lt;/p&gt;
&lt;p&gt;Transporting the food is another cost center; a constant question is then "How to get transportation costs down to zero?"&lt;/p&gt;
&lt;p&gt;One might even posit that having people create and sell food is a cost center: why not robots? &lt;em&gt;(of course then whomever maintains the robots is the cost center)&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Profit_center"&gt;https://en.wikipedia.org/wiki/Profit_center&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Cost_centre_(business)"&gt;https://en.wikipedia.org/wiki/Cost_centre_(business)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since Profit Centers are what everyone seeks, they are harder to find. They depend on Customers, on satisfying demand, on product market fit, etc... including many things that may be beyond your control.&lt;/p&gt;
&lt;p&gt;Cost Centers tend to provide the illusion of control (things you are paying for) and so are eminently more enjoyable to tweak and revise, presumably with less risk.&lt;/p&gt;
&lt;h3 id="you-cannot-save-your-way-into-riches"&gt;You cannot save your way into riches&lt;/h3&gt;
&lt;p&gt;If you save $1 a day from some habit or vice, you will have saved $7,300 in 20 years.&lt;/p&gt;
&lt;p&gt;Despite the sage advice to save money, one should be aware that compound interest is not magic.&lt;/p&gt;
&lt;p&gt;If you start with $10,000 and compound annually at 10% interest for 20 years you will have $67,275.  To further debunk: starting with $10k is not entirely trivial, and I suspect nobody will be famous for having $67k 20 years from now. In fact, with inflation, I suspect very many people will have at least $67k which is a far cry from $1000k (aka $1m), and even a millionaire isn't as glamarous a title as it used to be.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.investor.gov/financial-tools-calculators/calculators/compound-interest-calculator"&gt;https://www.investor.gov/financial-tools-calculators/calculators/compound-interest-calculator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Therefore to be rich and profitable you must make money. &lt;em&gt;(Perhaps an unsurprising conclusion but for the penny pinchers)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So a Cost Center improvement of 100%, on a company with $0 revenue, only reduces how fast your plane plummets to the earth.&lt;/p&gt;
&lt;h3 id="when-to-invest-in-a-cost-center"&gt;When to invest in a Cost Center&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;There is an opportunity cost to being alive&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Time is not infinite. If you are focusing on your cost center you may be neglecting your profit center, yet there are definitely moments when the investment is worth it.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When the Cost Center deficiencies become a distraction: if your business does not have electricity or your employees are lost in confusion and chaos then it seems much more challenging to expect your Profit Center to succeed.&lt;/li&gt;
&lt;li&gt;When the Cost Center is a strategic differentiator: it helps create the moat that others cannot cross. As an example: Amazon began offering free shipping (and escalating into 2-day, then 1-day, then hours etc.), which certainly costs a lot to do. Yet it reduced friction to purchasing (and enabled the very profitable Amazon Prime subscription service) which increased sales, strategically raising the bar for competitors to "have a digital platform for buying things". &lt;a href="https://www.vox.com/recode/2019/5/3/18511544/amazon-prime-oral-history-jeff-bezos-one-day-shipping"&gt;https://www.vox.com/recode/2019/5/3/18511544/amazon-prime-oral-history-jeff-bezos-one-day-shipping&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;When the Cost Center will become your Profit Center, see Slack's business pivot away from creating an online game (presumably for profit) into collaborative realtime messaging (an internal tool that cost them money to build and operate). &lt;a href="https://mastersofscale.com/stewart-butterfield-the-big-pivot/"&gt;https://mastersofscale.com/stewart-butterfield-the-big-pivot/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;When you need to explain to an Investor (present or future) that you are not wasteful, i.e. efficiency as a proxy for smarts/effectiveness.&lt;/li&gt;
&lt;li&gt;When you have revenue but your cost center is creating a risk to your cashflow, e.g. you are spending so much it seems unlikely you will be able to last long enough for your profit center to profit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;TODO: add more good reasons, but do it later to avoid over-investing in a cost center ;p&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="bad-cashflow-versus-bad-business"&gt;Bad Cashflow versus Bad Business&lt;/h2&gt;
&lt;p&gt;A bad business model will lose money until it is all gone. Interestingly it is possible to have a good business model and still go out of business.&lt;/p&gt;
&lt;p&gt;Bad cashflow can kill a good business: you owe more money in a given month than you can pay your creditors and they want their payment now. Over a longer time horizon a good business model would have made everybody more money.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Infinite time horizons are often similar to ponzi schemes where someone has to keep being the "new sucker born every minute".&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Thus the stereotype of a business person being conservative and frugal; they're controlling their cashflow, earning through profit over time.&lt;/p&gt;
&lt;p&gt;Contrast with a stereotypical "great entrepeneur" who takes risks and creates profit.&lt;/p&gt;
&lt;p&gt;Both approaches have the potential for being a bad business.&lt;/p&gt;
&lt;p&gt;How can you discern when "losing money is fine"? There's the usual routine of examining timelines (months and up to a few years - further than that and your crystal ball is the "Profit Center"), then considering risks, growth projections, competition, etc.&lt;/p&gt;
&lt;p&gt;One simple technical approach is to see how much improvement can occur to the Cost Center and the impact on the Profit Center.&lt;/p&gt;
&lt;p&gt;If you can invest and immediately improve your cashflow then you have a straightforward plan for improvement.&lt;/p&gt;
&lt;p&gt;As mentioned earlier, a bad business model does not have a Profit Center. Having no positive cashflow may be something that can be fixed.&lt;/p&gt;
&lt;h3 id="should-you-work-in-a-cost-center"&gt;Should you work in a Cost Center?&lt;/h3&gt;
&lt;p&gt;Sensibly a business needs to create revenue &lt;strong&gt;and&lt;/strong&gt; reduce costs, so someone needs to work in the "Cost Center". If you are irresponsible and careless it seems unlikely you will be successful in a Cost Center.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You may genuinely enjoy innovating on efficiency; the larger the scale or harder the problem then potentially the greater the impact.&lt;/li&gt;
&lt;li&gt;The company may have a cashflow issue and working in the Cost Center may save the company.&lt;/li&gt;
&lt;li&gt;It may be that the opportunity to join a growing/successful company is in the Cost Center; don't look a gift horse in the mouth. (A rising tide lifts all ships).&lt;/li&gt;
&lt;li&gt;Sometimes working in a Cost Center is a way to learn the foundations of a business - where are all the dependencies that created the situation for profit?&lt;/li&gt;
&lt;li&gt;In the "DevOps" (and TDD) culture there's a deliberate attempt to remove the artificial silos that would have created a "cost center" (like QA or Operations) in the first place by distributing the work (and automating the toil); everybody should work in a Cost center!&lt;/li&gt;
&lt;/ul&gt;</content><category term="it"/><category term="business"/><category term="cost center"/><category term="profit center"/><category term="cashflow"/><category term="management"/></entry><entry><title>Sorting in Golang</title><link href="https://blog.john-pfeiffer.com/sorting-in-golang/" rel="alternate"/><published>2020-09-30T00:00:00-07:00</published><updated>2020-09-30T00:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2020-09-30:/sorting-in-golang/</id><summary type="html">
&lt;p&gt;We maybe take for granted that humans sort things: having a list of favorites e.g. "top ten songs".&lt;/p&gt;
&lt;p&gt;Computers (and software) are exceptional for sorting since it is both boring and requires precision; especially for large data sets that are beyond the human brain.&lt;/p&gt;
&lt;p&gt;Sorting things (not just numbers …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;We maybe take for granted that humans sort things: having a list of favorites e.g. "top ten songs".&lt;/p&gt;
&lt;p&gt;Computers (and software) are exceptional for sorting since it is both boring and requires precision; especially for large data sets that are beyond the human brain.&lt;/p&gt;
&lt;p&gt;Sorting things (not just numbers!) is foundational in Computer Science, &lt;a href="https://en.wikipedia.org/wiki/Sorting_algorithm"&gt;https://en.wikipedia.org/wiki/Sorting_algorithm&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Sorting is often considered a "free first step" (since sorting is "Big O n logn") for many problems that already would take a least "n logn" to solve.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Golang has made Sorting really easy and efficient. &lt;em&gt;Both in terms of compute and memory&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="verbose-implementation-of-sorting-with-go-primitives-like-a-map"&gt;Verbose implementation of Sorting with Go Primitives like a Map&lt;/h2&gt;
&lt;p&gt;First, if you want to just stick with primitives, you can sort a particular item like this...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"sort"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Person is defined by name and age&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Charles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original Listing: %v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Example of manually sorting using extra primitive data structures&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;AgeToPeople&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;][]&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// handle the edge case of not yet having a list entry in the map&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AgeToPeople&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;AgeToPeople&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AgeToPeople&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;AgeToPeople&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// this maintains the pre-existing order on duplicates&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Sort by a property and then dereference the lookup map&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// get the unique list of ages (each map key is unique)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Ages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AgeToPeople&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;Ages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Ages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Ints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Ages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PeopleSortedByAge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Ages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AgeToPeople&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;PeopleSortedByAge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PeopleSortedByAge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sorted by age Listing: %v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PeopleSortedByAge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Optionally create a subset of the "lowest N members"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// this could be more efficient if it was applied during the PeopleSortedByAge loop&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;PeopleSortedByAgeSubset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PeopleSortedByAge&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Stable Subset of Sorted by age: %v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PeopleSortedByAgeSubset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This code is verbose but does provide visibility and control:
Not modifying the original slice, the exact property used for sorting, explicit handling of duplicates aka the "stable sort" property&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Sorting_algorithm#Stability"&gt;https://en.wikipedia.org/wiki/Sorting_algorithm#Stability&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;ok, I am "hand-waving" when we use the built in sorting of Integers but I'm not looking to re-invent the wheel...&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To execute (or modify) the code yourself &lt;a href="https://play.golang.org/p/A9IYxubb8yK"&gt;https://play.golang.org/p/A9IYxubb8yK&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="very-easy-and-fast-way-to-sort-in-go"&gt;Very easy and fast way to Sort in Go&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"sort"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Person is defined by name and age&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Charles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original Listing: %v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SliceStable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original Sorted using BuiltIn SliceStable: %v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The anonymous function is passed in as a parameter to define how to compare elements in the slice
and be aware that the Sort function modifies the original slice&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This sorting capability comes built in with the Go Standard Library &lt;a href="https://golang.org/pkg/sort/#SliceStable"&gt;https://golang.org/pkg/sort/#SliceStable&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="why-sorting-with-go-is-efficient"&gt;Why sorting with Go is efficient&lt;/h3&gt;
&lt;p&gt;Leveraging a well established implementation of sort saves a lot of developer time (and provides some guarantee of correctness).&lt;/p&gt;
&lt;p&gt;As a compiled language Go is performant in terms of compute (which often translates to reasonably fast wall clock time too).&lt;/p&gt;
&lt;p&gt;The use of Slices means under the hood there can be pointers and references rather than full copies which reduces memory consumption.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/slices-intro"&gt;https://blog.golang.org/slices-intro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/slices"&gt;https://blog.golang.org/slices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/src/sort/sort.go"&gt;https://golang.org/src/sort/sort.go&lt;/a&gt; (quicksort but it could be any highly performant sort)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="sorting-objects-in-go-with-customization"&gt;Sorting objects in Go with Customization&lt;/h2&gt;
&lt;p&gt;It is very powerful is to leverage Golang's interface capabilities and the Sort package.&lt;/p&gt;
&lt;p&gt;Since Go uses composition instead of inheritance any arbitrary data structure can support Sorting.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://talks.golang.org/2012/splash.article#TOC_15."&gt;https://talks.golang.org/2012/splash.article#TOC_15.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/sort/"&gt;https://golang.org/pkg/sort/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thus with a struct definition, then writing a few method definitions, one can sort a collection of objects.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"sort"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Person is defined by name and age&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Charles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original Listing: %v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ByAge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original Sorted using a Customized Less: %v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;People&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ByAge is a list of Persons that can be sorted by age&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ByAge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;

&lt;span class="c1"&gt;// Len implements the interface for Sort&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ByAge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Swap implements the interface for Sort&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ByAge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Swap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Less implements the interface for Sort&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ByAge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Less&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// customize sorting on age equivalence to use Name too&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// otherwise ages are not equal, simply use age&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This "strategy pattern" allows Golang to provide Sorting as an abstraction&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Strategy_pattern#Strategy_and_open/closed_principle"&gt;https://en.wikipedia.org/wiki/Strategy_pattern#Strategy_and_open/closed_principle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The ability to customize logic in &lt;strong&gt;Less()&lt;/strong&gt; is very powerful, yet callers need only pass an extra &lt;strong&gt;ByAge()&lt;/strong&gt; to benefit.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;To execute (or modify) the code yourself use &lt;a href="https://play.golang.org/p/A9IYxubb8yK"&gt;https://play.golang.org/p/A9IYxubb8yK&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If I ever put more time into various ideas or helper functions it will end up here &lt;a href="https://github.com/johnpfeiffer/go-sort-example"&gt;https://github.com/johnpfeiffer/go-sort-example&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="sorting-in-reverse"&gt;Sorting in Reverse&lt;/h2&gt;
&lt;p&gt;There is a helper function that just reverses the sorting order, so once you can Sort (because you fulfill the Interface), then you can "reverse sort" =)
&lt;strong&gt;alternatively you could use a for loop and iterate over a sorted slice, starting from index length-1 down to 0&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"sort"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// convert a to an IntSlice so it can be Reverse Sorted&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IntSlice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Typically this is how to control "ascending" or "descending" sorted order&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/sort/#Reverse"&gt;https://golang.org/pkg/sort/#Reverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/EdVA6NzNRiF"&gt;https://play.golang.org/p/EdVA6NzNRiF&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="sorting-a-map-in-go"&gt;Sorting a Map in Go&lt;/h2&gt;
&lt;h3 id="sorting-a-map-by-keys"&gt;Sorting a map by Keys&lt;/h3&gt;
&lt;p&gt;To sort by keys is relatively trivial, for a given map "m" creating a slice and sorting it&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"z"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"zebra"&lt;/span&gt;
&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"aa"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Aardvark"&lt;/span&gt;
&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Bear"&lt;/span&gt;
&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"antelope"&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sortedKeys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sortedKeys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Strings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sortedKeys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sortedKeys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s %s "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="sorting-a-map-by-values"&gt;Sorting a map by Values&lt;/h3&gt;
&lt;p&gt;Sorting a map by values is a little harder which is likely why a cursory internet search brings up far fewer code snippet examples ;)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// SortByValue returns the Keys in the sorted order of the Values (does not guarantee Stability)&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SortByValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sortedKeys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="c1"&gt;// create a reverse lookup mapping that can handle collisions&lt;/span&gt;
&lt;span class="c1"&gt;// (duplicates) when the same value has multiple occurrences&lt;/span&gt;
&lt;span class="nx"&gt;ValueToKey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;][]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ValueToKey&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;ValueToKey&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ValueToKey&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;ValueToKey&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// sort the Values&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;uniqueValues&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ValueToKey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;uniqueValues&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uniqueValues&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Strings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uniqueValues&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// get the corresponding Keys&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;uniqueValues&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ValueToKey&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sortedKeys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sortedKeys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The gotcha is that different Keys could have the same Value&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Full source code examples: &lt;a href="https://github.com/johnpfeiffer/go-sort-example"&gt;https://github.com/johnpfeiffer/go-sort-example&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="go"/><category term="golang"/><category term="sort"/><category term="sorting"/><category term="map"/></entry><entry><title>Using AWS CDK to configure deploy a Golang Lambda with APIGateway</title><link href="https://blog.john-pfeiffer.com/using-aws-cdk-to-configure-deploy-a-golang-lambda-with-apigateway/" rel="alternate"/><published>2020-09-27T14:48:00-07:00</published><updated>2020-09-27T14:48:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2020-09-27:/using-aws-cdk-to-configure-deploy-a-golang-lambda-with-apigateway/</id><summary type="html">
&lt;p&gt;Infrastructure as Code helps guarantee the elusive determinism in infrastructure that we all seek in building applications and services.
Often you can defer that complexity by using Heroku, Google App Engine,  or other PaaS providers. But when you need to build something really complex (or with specific controls required including …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Infrastructure as Code helps guarantee the elusive determinism in infrastructure that we all seek in building applications and services.
Often you can defer that complexity by using Heroku, Google App Engine,  or other PaaS providers. But when you need to build something really complex (or with specific controls required including managing costs) then using IaaC to tame AWS Serverless reduces some of that pain.&lt;/p&gt;
&lt;p&gt;AWS have created their own specific product (domain specific language) "CDK" which competes with the venerable Terraform and nicely focused Serverless.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;All of these products use the json syntax of CloudFormation which is the foundational AWS syntax for describing resources&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It is straightforward to iteratively setup and use AWS CDK (with the native Typescript syntax).&lt;/p&gt;
&lt;h2 id="cdk-vocabulary"&gt;CDK vocabulary&lt;/h2&gt;
&lt;p&gt;It all begins with &lt;strong&gt;stacks&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;From an object oriented perspective, a stack is a definition of components that can be instantiated. An "app" is a collection of related stacks.&lt;/p&gt;
&lt;p&gt;Let's say you wanted your persistence (DynamoDB) in one stack, and your more execution oriented components (APIGateway and Lambda) in another stack, and your App might have environment specific parameters it needs to define and pass through to each stack.&lt;/p&gt;
&lt;p&gt;This ability to use templates and customize how you structure things, reference other CDK files, etc. makes CDK very modular and re-usable (and yes Typescript is a programming language so you can have linters and tests).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/stacks.html"&gt;https://docs.aws.amazon.com/cdk/latest/guide/stacks.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://intro-to-cdk.workshop.aws/what-is-cdk.html"&gt;https://intro-to-cdk.workshop.aws/what-is-cdk.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="install-the-aws-cdk-tool"&gt;Install the AWS CDK Tool&lt;/h2&gt;
&lt;p&gt;Have to use npm to install the cdk tool...&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install -g aws-cdk&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/cli.html"&gt;https://docs.aws.amazon.com/cdk/latest/guide/cli.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="setup-the-project-directories"&gt;Setup the project directories&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;mkdir cdk-example&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cd cdk-example&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Create a subdirectory to compartmentalize all the infrastructure code &lt;em&gt;(from the rest of the application, i.e. don't mix the business logic with the infrastructure plumbing)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mkdir infra&lt;/code&gt;
&lt;code&gt;cd infra&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cdk init app --language typescript&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;creates the default files in this directory for an empty CDK app&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cdk ls&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;   InfraStack
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Names are hard but whatever you pick, like &lt;strong&gt;"Infra"&lt;/strong&gt;, will then show up in the AWS resources everywhere related to this project&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="install-the-dependencies-for-aws-resources-that-cdk-will-manage"&gt;Install the dependencies for AWS resources that CDK will manage&lt;/h2&gt;
&lt;p&gt;Option 1: manually install dependencies &lt;em&gt;(it will automatically insert this into package.json)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install @aws-cdk/aws-s3 @aws-cdk/aws-lambda&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Option 2: write the lines into packages.json and run at the command line in the infra directory: &lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Writing files first (and ensuring they are in version control) is a more IaaC pattern&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"@aws-cdk/aws-s3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"@aws-cdk/core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"source-map-support"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.5.16"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="write-the-actual-infrastructure-code"&gt;Write the actual Infrastructure Code&lt;/h2&gt;
&lt;p&gt;The initial application template creates an empty "class" that represents the "stack", we customize and replace that code with the following...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;lib/infra-stack.ts&lt;/strong&gt; has only 2 changes to the default file, the import of S3 and the new Bucket resource "MyExampleBucket"&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'@aws-cdk/core'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'@aws-cdk/aws-s3'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;InfraStack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;cdk.Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;props?&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;cdk.StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'MyExampleBucket'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;versioned&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The import renames are important to be consistent in subsequent code, so if something is "cdk" then it is cdk.Construct&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;More complex applications will use a "main entry point" that can refer to specific files for various stacks.
- &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/serverless_example.html"&gt;https://docs.aws.amazon.com/cdk/latest/guide/serverless_example.html&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="output-cloudformation-from-a-cdk-typescript-file"&gt;Output CloudFormation from a CDK Typescript file&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;cdk synth&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;this command will output to the display the CloudFormation that will be sent/used by AWS&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;Resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;MyExampleBucket8D68EFCA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;AWS::S3::Bucket&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;VersioningConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Enabled&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;UpdateReplacePolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Retain&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;DeletionPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Retain&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;aws:cdk:path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;InfraStack/MyExampleBucket/Resource&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;CDKMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;AWS::CDK::Metadata&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To preview what will occur between changes to the .ts file...&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cdk diff&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="deploy-resources-that-cdk-has-defined"&gt;Deploy resources that CDK has defined&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;cdk deploy&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;warning, really making a change in AWS (based on your creds)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;InfraStack

InfraStack:&lt;span class="w"&gt; &lt;/span&gt;deploying...
InfraStack:&lt;span class="w"&gt; &lt;/span&gt;creating&lt;span class="w"&gt; &lt;/span&gt;CloudFormation&lt;span class="w"&gt; &lt;/span&gt;changeset...
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;/3&lt;span class="o"&gt;)&lt;/span&gt;

Stack&lt;span class="w"&gt; &lt;/span&gt;ARN:
arn:aws:cloudformation:us-east-1:409670809604:stack/InfraStack/b5167030-00fb-11eb-9f36-12f8925a37c4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="verify-the-new-bucket-was-created"&gt;Verify the new bucket was created&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;aws s3 ls | grep MyExampleBucket&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;this assumes you have installed the AWS CLI like &lt;code&gt;sudo apt install awscli&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aws cloudformation list-stacks | grep InfraStack&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;this listing will also show deleted Stacks&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="making-updates-with-cdk"&gt;Making updates with CDK&lt;/h2&gt;
&lt;p&gt;A tiny snippet change to allow bucket deletion...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'MyExampleBucket'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;versioned&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;removalPolicy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;cdk.RemovalPolicy.DESTROY&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/hello_world.html"&gt;https://docs.aws.amazon.com/cdk/latest/guide/hello_world.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cdk/api/latest/typescript/api/aws-s3.html"&gt;https://docs.aws.amazon.com/cdk/api/latest/typescript/api/aws-s3.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cdk/api/latest/typescript/api/aws-s3/bucketpolicyprops.html#aws_s3_BucketPolicyProps"&gt;https://docs.aws.amazon.com/cdk/api/latest/typescript/api/aws-s3/bucketpolicyprops.html#aws_s3_BucketPolicyProps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cdk/api/latest/typescript/api/core/removalpolicy.html#core_RemovalPolicy"&gt;https://docs.aws.amazon.com/cdk/api/latest/typescript/api/core/removalpolicy.html#core_RemovalPolicy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="preview-changes-with-diff"&gt;Preview changes with diff&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;cdk diff&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Stack&lt;span class="w"&gt; &lt;/span&gt;InfraStack
Resources
&lt;span class="o"&gt;[&lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;AWS::S3::Bucket&lt;span class="w"&gt; &lt;/span&gt;JohnPBucket&lt;span class="w"&gt; &lt;/span&gt;JohnPBucket8D68EFCA
&lt;span class="w"&gt; &lt;/span&gt;├─&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DeletionPolicy
&lt;span class="w"&gt; &lt;/span&gt;│&lt;span class="w"&gt;   &lt;/span&gt;├─&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;-&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Retain
&lt;span class="w"&gt; &lt;/span&gt;│&lt;span class="w"&gt;   &lt;/span&gt;└─&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;+&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Delete
&lt;span class="w"&gt; &lt;/span&gt;└─&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;UpdateReplacePolicy
&lt;span class="w"&gt;     &lt;/span&gt;├─&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;-&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Retain
&lt;span class="w"&gt;     &lt;/span&gt;└─&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;+&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Delete
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="apply-the-new-changes"&gt;Apply the new changes&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;cdk deploy&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;InfraStack:&lt;span class="w"&gt; &lt;/span&gt;deploying...
InfraStack:&lt;span class="w"&gt; &lt;/span&gt;creating&lt;span class="w"&gt; &lt;/span&gt;CloudFormation&lt;span class="w"&gt; &lt;/span&gt;changeset...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;cdk destroy&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aws s3 ls | grep MyExampleBucket&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aws cloudformation list-stacks | grep InfraStack&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;CDK enables "InfrastructureAsCode" and command line (or scripted) resource management, yet you must still understand the intricacies of the domain (i.e. that s3 buckets have policies and do not get destroyed by default)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;s3 buckets are designed by default to not delete with a Stack, you must change the removal policy to do so&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2 id="apigateway-plus-lambda-plus-go"&gt;APIGateway plus Lambda plus Go&lt;/h2&gt;
&lt;p&gt;The trick to using Golang with AWS Lambdas is that it is a compiled language. Many tutorials for lambdas (even for CDK) use javascript or python since those dynamic languages can just be put in "one more file" without a build step.&lt;/p&gt;
&lt;h3 id="a-tiny-go-web-request-handler"&gt;A tiny Go Web Request Handler&lt;/h3&gt;
&lt;p&gt;Put a simple placeholder Golang Lambda in place &lt;em&gt;using the Gin web framework for convenience&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mkdir cdk-example/examplefunction/ ; cd cdk-example/examplefunction/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vim main.go&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"context"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"log"&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"github.com/aws/aws-lambda-go/events"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"github.com/aws/aws-lambda-go/lambda"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"github.com/awslabs/aws-lambda-go-api-proxy/gin"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"github.com/gin-gonic/gin"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ginLambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;ginadapter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GinLambda&lt;/span&gt;

&lt;span class="c1"&gt;// for convenience leverage the Go init startup concept to define a global web server object&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// stdout and stderr are sent to AWS CloudWatch Logs&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Gin starting"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;gin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/ping"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;gin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;gin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;H&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"pong"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;ginLambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ginadapter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;// Handler is the function that executes for every Request passed into the Lambda&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIGatewayProxyRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIGatewayProxyResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ginLambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProxyWithContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;go mod init&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ensure dependencies (like the AWS SDK) are recognized by the Go package manager&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;go test&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is the simplest way to trigger downloading the dependencies (imported packages), you may have to create a tiny main_test.go in order to force this to work&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://blog.golang.org/using-go-modules"&gt;https://blog.golang.org/using-go-modules&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="manually-build-your-go-binary-for-aws-lambda"&gt;Manually build your go binary for AWS Lambda&lt;/h4&gt;
&lt;p&gt;This will create an output file named "main"&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GOOS=linux GOARCH=amd64 go build -o main main.go&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;compile for the target arch , AWS Lambda ("firecracker") is Linux =]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.usenix.org/system/files/nsdi20-paper-agache.pdf"&gt;https://www.usenix.org/system/files/nsdi20-paper-agache.pdf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;zip examplefunction.zip main&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AWS requires these precompiled binaries to in .zip format&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="cdk-with-a-golang-lambda"&gt;CDK with a Golang Lambda&lt;/h3&gt;
&lt;p&gt;Focusing on just the "infra" subdirectory in our project:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;cdk-example/infra/package.json&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cdk-example/infra/lib/infra-stack.ts&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;cd cdk-example/infra/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;First update &lt;strong&gt;package.json&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="s2"&gt;"dependencies"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"@aws-cdk/core"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"@aws-cdk/aws-s3"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"@aws-cdk/aws-s3-assets"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"@aws-cdk/aws-lambda"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"source-map-support"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.5.16"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Do not forget to &lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Next update the &lt;strong&gt;lib/infra-stack.ts&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Layout the resources from the deepest dependency first, so in this case a place for the golang function to be zipped&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'@aws-cdk/core'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'@aws-cdk/aws-s3'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'@aws-cdk/aws-lambda'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;assets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"@aws-cdk/aws-s3-assets"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;InfraStack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;cdk.Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;props?&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;cdk.StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Golang binaries must have a place where they are uploaded to s3 as a .zip&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'ExampleFunctionZip'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;path.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'../../examplefunction.zip'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ExampleFunction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;lambda.Runtime.GO_1_X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;lambda.Code.fromBucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3ObjectKey&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This crucial glue code indicates that the Lambda will be named "ExampleFunction", that it will get the binary (zipped) from S3, and that the handler expects to have a binary "main"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note that CDK will handle actually uploading the .zip to an s3 bucket&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cdk/api/latest/docs/aws-s3-assets-readme.html"&gt;https://docs.aws.amazon.com/cdk/api/latest/docs/aws-s3-assets-readme.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cdk/api/latest/docs/aws-lambda-readme.html#handler-code"&gt;https://docs.aws.amazon.com/cdk/api/latest/docs/aws-lambda-readme.html#handler-code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;cdk synth&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;outputting the CloudFormation is a quick way to valied the syntax and see any warnings&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cdk diff&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;outputting and previewing the changes that will appear in AWS&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cdk deploy&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;InfraStack:&lt;span class="w"&gt; &lt;/span&gt;deploying...
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;%&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;start:&lt;span class="w"&gt; &lt;/span&gt;Publishing&lt;span class="w"&gt; &lt;/span&gt;05e95f6b38c932a779e68a7a685e9950eca688e775c77f84787f6fa3e2ade474:current
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;%&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;success:&lt;span class="w"&gt; &lt;/span&gt;Published&lt;span class="w"&gt; &lt;/span&gt;05e95f6b38c932a779e68a7a685e9950eca688e775c77f84787f6fa3e2ade474:current
InfraStack:&lt;span class="w"&gt; &lt;/span&gt;creating&lt;span class="w"&gt; &lt;/span&gt;CloudFormation&lt;span class="w"&gt; &lt;/span&gt;changeset...
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;/4&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;InfraStack

Stack&lt;span class="w"&gt; &lt;/span&gt;ARN:
arn:aws:cloudformation:us-east-1:409670809604:stack/InfraStack/248d2960-0104-11eb-8cc5-0ac853a0932f
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Use the AWS Console UI and visually look at the cloud formation stacks&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;e.g. &lt;a href="https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/"&gt;https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When you filter and click on your new stack (e.g. InfraStack) you will be able to see the Resources associated with that stack&lt;/p&gt;
&lt;p&gt;The default policy "AWSLambdaBasicExecutionRole" will actually allow the Lambda to output logs to Cloudwatch&lt;/p&gt;
&lt;h3 id="cdk-with-an-apigateway-integrated-with-a-go-lambda"&gt;CDK with an APIGateway integrated with a Go Lambda&lt;/h3&gt;
&lt;p&gt;Create the APIG and attach it to the Lambda:&lt;/p&gt;
&lt;p&gt;First update &lt;strong&gt;infra/package.json&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"dependencies"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"@aws-cdk/aws-apigateway"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"@aws-cdk/aws-lambda"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"@aws-cdk/aws-s3"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"@aws-cdk/aws-s3-assets"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"@aws-cdk/core"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"source-map-support"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.5.16"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Define the API Gateway in CDK &lt;em&gt;(only adding a few more lines)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/cdk/api/latest/docs/aws-apigateway-readme.html#aws-lambda-backed-apis"&gt;https://docs.aws.amazon.com/cdk/api/latest/docs/aws-apigateway-readme.html#aws-lambda-backed-apis&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'@aws-cdk/core'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'@aws-cdk/aws-s3'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'@aws-cdk/aws-lambda'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;assets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"@aws-cdk/aws-s3-assets"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;apigw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"@aws-cdk/aws-apigateway"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;InfraStack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;cdk.Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;props?&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;cdk.StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Golang binaries must have a place where they are uploaded to s3 as a .zip&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'ExampleFunctionZip'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;path.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'../../examplefunction'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myhandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ExampleFunction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;lambda.Runtime.GO_1_X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;lambda.Code.fromBucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3ObjectKey&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// all routes (and REST verbs) will pass through to the lambda&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;apigw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LambdaRestApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'examplefunction'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;myhandler&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;one gotcha is the ordering of imports, have the "path" one last&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cdk synth&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;now the CloudFormation is very verbose =|&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;upon inspection you will see the type of integration Lambda is as desired, AWS_PROXY&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cdk deploy&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is a warning about security because AssumeRole&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;InfraStack:&lt;span class="w"&gt; &lt;/span&gt;deploying...
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;%&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;start:&lt;span class="w"&gt; &lt;/span&gt;Publishing&lt;span class="w"&gt; &lt;/span&gt;05e95f6b38c932a779e68a7a685e9950eca688e775c77f84787f6fa3e2ade474:current
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;%&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;success:&lt;span class="w"&gt; &lt;/span&gt;Published&lt;span class="w"&gt; &lt;/span&gt;05e95f6b38c932a779e68a7a685e9950eca688e775c77f84787f6fa3e2ade474:current
InfraStack:&lt;span class="w"&gt; &lt;/span&gt;creating&lt;span class="w"&gt; &lt;/span&gt;CloudFormation&lt;span class="w"&gt; &lt;/span&gt;changeset...
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;/14&lt;span class="o"&gt;)&lt;/span&gt;
InfraStack

Outputs:
InfraStack.examplefunctionEndpoint65D9943D&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;https://abc123.execute-api.us-east-1.amazonaws.com/prod/

Stack&lt;span class="w"&gt; &lt;/span&gt;ARN:
arn:aws:cloudformation:us-east-1:409670809604:stack/InfraStack/248d2960-0104-11eb-8cc5-0ac853a0932f
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="verify-your-new-api-gateway-and-golang-lambda-with-curl"&gt;Verify your new API Gateway and Golang Lambda with CURL&lt;/h2&gt;
&lt;p&gt;The very helpful output means you can use &lt;code&gt;CURL&lt;/code&gt; directly &lt;em&gt;(as you get to ignore the whole "this apigateway needs to be deployed to a stage")&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl https://abc123.execute-api.us-east-1.amazonaws.com/prod/ping&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;you should get back either 404 or "pong" =)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To see how much more work it is to do (and understand) the API Gateway concepts... &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/localstack-apigateway-lambda-and-s3-integration-testing/"&gt;https://blog.john-pfeiffer.com/localstack-apigateway-lambda-and-s3-integration-testing/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="build-CI-CD-devops"/><category term="go"/><category term="golang"/><category term="aws"/><category term="lambda"/><category term="apigateway"/><category term="cdk"/></entry><entry><title>Localstack APIGateway Lambda and S3 integration testing</title><link href="https://blog.john-pfeiffer.com/localstack-apigateway-lambda-and-s3-integration-testing/" rel="alternate"/><published>2020-03-05T21:10:00-08:00</published><updated>2020-03-05T21:10:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2020-03-05:/localstack-apigateway-lambda-and-s3-integration-testing/</id><summary type="html">
&lt;p&gt;One of the challenges with "serverless" is how to develop locally, especially things like running integration tests in your dev environment.&lt;/p&gt;
&lt;p&gt;Imagine writing code for a lambda that reads and writes from S3, but without any AWS.&lt;/p&gt;
&lt;p&gt;The AWS tool SAM does have a local mode but does not cover …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;One of the challenges with "serverless" is how to develop locally, especially things like running integration tests in your dev environment.&lt;/p&gt;
&lt;p&gt;Imagine writing code for a lambda that reads and writes from S3, but without any AWS.&lt;/p&gt;
&lt;p&gt;The AWS tool SAM does have a local mode but does not cover S3 nor Dynamo, etc.&lt;/p&gt;
&lt;p&gt;A great tool to fill this need is Localstack.  Since its interfaces are compatible with AWS it is an excellent proxy.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Mirroring environments of Development, Staging, and Production, along with Integration or Acceptance tests, are best practices that allow you to write code with confidence and catch issues much earlier (and therefore more cheaply) than "Using your Users as QA in Production"&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Background: my previous article about writing a lambda with Golang and deploying it to AWS.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/go-faas-with-aws-lambda/"&gt;https://blog.john-pfeiffer.com/go-faas-with-aws-lambda/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="install-and-configure-the-aws-cli"&gt;Install and configure the AWS CLI&lt;/h3&gt;
&lt;p&gt;In order to interact with Localstack we will use the AWS CLI, welcome to the beauty of Interfaces (API driven development ;)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo apt install awscli&lt;/code&gt; &lt;em&gt;or for all the alternate installation options &lt;a href="https://aws.amazon.com/cli/"&gt;https://aws.amazon.com/cli/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Setup fake credentials...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;this will overwrite any existing ~/.aws/ config or credentials&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[default]\n\&lt;/span&gt;
&lt;span class="s2"&gt;region=us-east-1\n\&lt;/span&gt;
&lt;span class="s2"&gt;output=json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;~/.aws/config

&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[default]\n\&lt;/span&gt;
&lt;span class="s2"&gt;aws_access_key_id=AKIAFAKE\n\&lt;/span&gt;
&lt;span class="s2"&gt;aws_secret_access_key=FAKE"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;~/.aws/credentials
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html"&gt;https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="simplest-localstack-lambda-configuration"&gt;Simplest Localstack Lambda Configuration&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt;

&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;localstack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"localstack/localstack"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"4574:4574"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# lambda&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;SERVICES=lambda&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;LAMBDA_EXECUTOR=docker&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;DOCKER_HOST=unix:///var/run/docker.sock&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;DEBUG=1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"/var/run/docker.sock:/var/run/docker.sock"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;sudo docker-compose up&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Creating task_localstack_1 ... done
Attaching to task_localstack_1
localstack_1  | Waiting for all LocalStack services to be ready
localstack_1  | Starting mock Lambda service (http port 4574)...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/localstack/localstack"&gt;https://github.com/localstack/localstack&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="a-very-simple-go-lambda"&gt;A very simple Go Lambda&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;mkdir task; cd task; vim task.go&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;creates a directory/package and file with the same name to be the lambda code&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"github.com/aws/aws-lambda-go/lambda"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// MyEvent is a thing&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:"name"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// HandleRequest for an event&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HandleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hi %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HandleRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;go build&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;outputs a binary file "task", if on MacOS you may want to cross compile with: &lt;code&gt;GOOS=linux go build&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;zip task.zip task&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;lambda code uploads must be zipped in advance&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="create-function-aka-upload-the-golang-code-to-the-localstack-lambda"&gt;Create-Function aka Upload the golang code to the localstack lambda&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;aws --endpoint-url=http://localhost:4574 lambda create-function --function-name=task --runtime="go1.x" --role=fakerole --handler=task --zip-file fileb://task.zip&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;localstack_1  | 2020-03-06T05:09:38:DEBUG:localstack.utils.common: Starting download from http://a46a9ed6f485:4574/2015-03-31/functions/task/code to /tmp/tmpfile.6f1d50ce9ccf62f3094d3c7f9eb82573/archive.zip (5060419 bytes)&lt;/p&gt;
&lt;p&gt;localstack_1  | 2020-03-06T05:09:38:DEBUG:localstack.utils.common: Writing 1048576 bytes (total 1048576) to /tmp/tmpfile.6f1d50ce9ccf62f3094d3c7f9eb82573/archive.zip&lt;/p&gt;
&lt;p&gt;localstack_1  | 2020-03-06T05:09:38:DEBUG:localstack.utils.common: Done downloading http://a46a9ed6f485:4574/2015-03-31/functions/task/code, response code 200, total bytes 5060419&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html"&gt;https://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that we used a fake role name "fakerole", localstack does not enforce IAM roles or permissions&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="get-the-dependency-docker-container-that-actually-executes-golang"&gt;Get the dependency docker container that actually executes Golang&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;docker pull lambci/lambda:go1.x&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Now you can invoke the Lambda with an input...&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aws lambda --endpoint-url=http://localhost:4574 invoke --function-name task --payload='{"Name": "world"}' --region=us-east-1 myout.log&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;{    "StatusCode": 200    }&lt;/p&gt;
&lt;p&gt;localstack_1  | 2020-03-06T05:45:22:DEBUG:localstack.services.awslambda.lambda_executors: Lambda arn:aws:lambda:us-east-1:000000000000:function:task result / log output:
"hi world"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="other-useful-commands-for-updating-or-deleting-your-localstack-lambda"&gt;Other useful commands for updating or deleting your localstack lambda&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;aws --endpoint-url=http://localhost:4574 lambda list-functions&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aws --endpoint-url=http://localhost:4574 lambda update-function-code --function-name=task --zip-file fileb://task.zip&lt;/code&gt;
&lt;code&gt;aws --endpoint-url=http://localhost:4574 lambda delete-function --function-name task&lt;/code&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id="api-gateway-and-lambda-with-localstack"&gt;API Gateway and Lambda with Localstack&lt;/h2&gt;
&lt;p&gt;A very simple bit of "handler" code to exemplify the AWS Lambda Proxy Integration&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"context"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"net/http"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"github.com/aws/aws-lambda-go/events"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"github.com/aws/aws-lambda-go/lambda"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// HandleRequest for an event https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html&lt;/span&gt;
&lt;span class="c1"&gt;// input object defined here https://godoc.org/github.com/aws/aws-lambda-go/events#APIGatewayProxyRequest&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HandleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIGatewayProxyRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIGatewayProxyResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DEBUG: %#v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIGatewayProxyResponse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HandleRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html"&gt;https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How to parse the inputs provided to the Lambda by the AWS API Gateway
&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format"&gt;https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Output for a (Proxy Integration) Lambda with APIGateway needs a specific JSON format
&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format"&gt;https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format&lt;/a&gt;
The custom reponse headers are where CORS can be configured&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;GOOS=linux go build &amp;amp;&amp;amp; zip task.zip task&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aws --endpoint-url=http://localhost:4574 lambda update-function-code --function-name=task --zip-file fileb://task.zip&lt;/code&gt;
&lt;code&gt;aws --endpoint-url=http://localhost:4574 lambda list-functions&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;arn:aws:lambda:us-east-1:000000000000:function:task&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Again invoke the Lambda, with an input...&lt;/strong&gt;
&lt;code&gt;aws lambda --endpoint-url=http://localhost:4574 invoke --function-name task --payload='{"body": "foobar"}' --region=us-east-1 myout.log&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;{"StatusCode": 200}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;p&gt;&lt;code&gt;aws --endpoint-url=http://localhost:4567 apigateway create-rest-api --name myapi&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"createdDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1583558847&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"apiKeySource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HEADER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"endpointConfiguration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s2"&gt;"EDGE"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"29a3p9encp"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;That "id" of this REST API is important throughout the rest of the commands&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;aws --endpoint-url=http://localhost:4567 apigateway get-resources --rest-api-id 29a3p9encp&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"62wy7bzofu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"resourceMethods"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The "id" for the "/" resource is used as the "parent" for adding a "child" resource&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;aws --endpoint-url=http://localhost:4567 apigateway create-resource --rest-api-id 29a3p9encp --parent-id 62wy7bzofu --path-part mywidget&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"pathPart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mywidget"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"resourceMethods"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jylycd8v4u"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"parentId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"62wy7bzofu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/mywidget"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;We have created a REST resource /mywidget&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;aws --endpoint-url=http://localhost:4567 apigateway put-method --rest-api-id 29a3p9encp --resource-id jylycd8v4u --http-method GET --authorization-type NONE&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"authorizationType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NONE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"httpMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Now /mywidget does not require any authentication for GET requests&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;aws --endpoint-url=http://localhost:4567 apigateway put-integration --rest-api-id 29a3p9encp --resource-id jylycd8v4u --http-method GET --integration-http-method POST --type AWS_PROXY --uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:task/invocations --passthrough-behavior WHEN_NO_MATCH&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS_PROXY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"httpMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:task/invocations"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"integrationResponses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"responseTemplates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This connects the AWS API Gateway GET /mywidget calls as a "proxy" (passing through the request) to the specified Lambda
POST is required &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html"&gt;https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;aws --endpoint-url=http://localhost:4567 apigateway create-deployment --rest-api-id 29a3p9encp --stage-name foobar&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"createdDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1583565386&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mbe3fwe0pw"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This actually activates the endpoint for traffic&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;p&gt;List everything that we have created...&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aws --endpoint-url=http://localhost:4567 apigateway get-resources --rest-api-id 29a3p9encp&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"resourceMethods"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"62wy7bzofu"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"resourceMethods"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nt"&gt;"methodIntegration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:task/invocations"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS_PROXY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;"httpMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;"integrationResponses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="nt"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                                &lt;/span&gt;&lt;span class="nt"&gt;"responseTemplates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                                    &lt;/span&gt;&lt;span class="nt"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="w"&gt;                                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;                                &lt;/span&gt;&lt;span class="nt"&gt;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nt"&gt;"httpMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nt"&gt;"authorizationType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NONE"&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"parentId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"62wy7bzofu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jylycd8v4u"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/mywidget"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;"pathPart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mywidget"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The fully configured API Gateway and /mywidget resource linked to the Lambda code&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/apigateway/test-invoke-method.html"&gt;https://docs.aws.amazon.com/cli/latest/reference/apigateway/test-invoke-method.html&lt;/a&gt;
&lt;code&gt;aws apigateway test-invoke-method --endpoint-url=http://localhost:4567 --rest-api-id 29a3p9encp --resource-id jylycd8v4u --http-method GET&lt;/code&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;&lt;code&gt;curl http://localhost:4567/restapis/29a3p9encp/&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"29a3p9encp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;"createdDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1583565481&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"apiKeySource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HEADER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;"endpointConfiguration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"EDGE"&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;curl -i http://localhost:4567/restapis/29a3p9encp/foobar/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl -i http://localhost:4567/restapis/29a3p9encp/foobar/_user_request_/mywidget&lt;/code&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Alternatively, HTTP_PROXY means you have to point it to a URI like https://example.com/my-existing-server&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/apigateway/create-rest-api.html"&gt;https://docs.aws.amazon.com/cli/latest/reference/apigateway/create-rest-api.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/apigateway/create-resource.html"&gt;https://docs.aws.amazon.com/cli/latest/reference/apigateway/create-resource.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/apigateway/put-method.html"&gt;https://docs.aws.amazon.com/cli/latest/reference/apigateway/put-method.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/apigateway/put-integration.html"&gt;https://docs.aws.amazon.com/cli/latest/reference/apigateway/put-integration.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/apigateway/create-deployment.html"&gt;https://docs.aws.amazon.com/cli/latest/reference/apigateway/create-deployment.html&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-create-api.html"&gt;https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-create-api.html&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html"&gt;https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format"&gt;https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;p&gt;TODO: YAML with APIGateway + Lambda + S3
- Go code for Request Event and write to S3
- curl example integration test&lt;/p&gt;
&lt;p&gt;Thanks to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/localstack/localstack/issues/561"&gt;https://github.com/localstack/localstack/issues/561&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.alexedwards.net/blog/serverless-api-with-go-and-aws-lambda"&gt;https://www.alexedwards.net/blog/serverless-api-with-go-and-aws-lambda&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="build-CI-CD-devops"/><category term="go"/><category term="golang"/><category term="localstack"/><category term="aws"/><category term="testing"/><category term="lambda"/><category term="apigateway"/><category term="s3"/><category term="integration testing"/><category term="docker-compose"/></entry><entry><title>Career Development and Software Engineering Roles</title><link href="https://blog.john-pfeiffer.com/career-development-and-software-engineering-roles/" rel="alternate"/><published>2018-12-31T12:34:00-08:00</published><updated>2018-12-31T12:34:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2018-12-31:/career-development-and-software-engineering-roles/</id><summary type="html">
&lt;h2 id="career-development-is-vague-but-heavy"&gt;Career Development is Vague but Heavy&lt;/h2&gt;
&lt;p&gt;It can be vague where someone's career was before they met you and how &lt;strong&gt;they&lt;/strong&gt; view their long term ambitions.&lt;/p&gt;
&lt;p&gt;And when the work is getting done &lt;strong&gt;now&lt;/strong&gt; it is easy to understand why &lt;strong&gt;future&lt;/strong&gt; Career Development is one of the overlooked aspects of …&lt;/p&gt;</summary><content type="html">
&lt;h2 id="career-development-is-vague-but-heavy"&gt;Career Development is Vague but Heavy&lt;/h2&gt;
&lt;p&gt;It can be vague where someone's career was before they met you and how &lt;strong&gt;they&lt;/strong&gt; view their long term ambitions.&lt;/p&gt;
&lt;p&gt;And when the work is getting done &lt;strong&gt;now&lt;/strong&gt; it is easy to understand why &lt;strong&gt;future&lt;/strong&gt; Career Development is one of the overlooked aspects of management.&lt;/p&gt;
&lt;p&gt;Knowledge workers have a lot of job choices: if their current trajectory won't help them meet their life goals they will make changes that may come as an unwelcome surprise to their manager or organization.&lt;/p&gt;
&lt;p&gt;Aligned interests, beyond salary-for-output &lt;em&gt;(it would be even better if it was compensation-for-outcome)&lt;/em&gt;, is one of the simpler ways to improve a product and reduce the costs of turnover. &lt;em&gt;(i.e. &lt;a href="https://en.wikipedia.org/wiki/Peopleware:_Productive_Projects_and_Teams"&gt;https://en.wikipedia.org/wiki/Peopleware:_Productive_Projects_and_Teams&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="when-to-discuss-career-development"&gt;When to discuss Career Development&lt;/h3&gt;
&lt;p&gt;One of the best tools for building relationships are 1:1's.
&lt;em&gt;("Manager Tools" &lt;a href="https://files.manager-tools.com/files/public/product-samples/One_on_One_Shownotes_0.pdf"&gt;https://files.manager-tools.com/files/public/product-samples/One_on_One_Shownotes_0.pdf&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;At least once a quarter &lt;em&gt;(aka 3 months)&lt;/em&gt; a 1:1 session should cover Career Development. &lt;em&gt;(i.e. a regular opportunity to align Company/Team Objectives or Goals with the individual's career)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;One of the first questions I generally ask is &lt;code&gt;"Where do you want to be in 2 years?"&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2 years is just long enough to be aspirational but near enough we should be planning together how to get there&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;"Where" should not be reductively just about salary, instead it should encompass talent + experience + direction.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As a caveat, even the largest companies will not have every type of role possible.
A good manager will provide guidance about what is immediately available (and research future possibilities).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Great managers grow careers even when it means the outcome will be an individual changing teams or organizations&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;h2 id="an-incomplete-list-of-technology-roles"&gt;An incomplete list of technology roles&lt;/h2&gt;
&lt;p&gt;There are too many roles related to technology to list them all but here are some important ones...&lt;/p&gt;
&lt;h3 id="software-engineer"&gt;Software Engineer&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Front End Engineer&lt;/strong&gt;: works mostly with implementing UserInterface features and bug fixes &lt;em&gt;(frequently using Javascript and including HTML and CSS)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mobile Engineer&lt;/strong&gt;: sort of a specialist "front end engineer" (assuming you agree that the focus is on Users), ideally experience with both iOS and Android but usually specializes in one&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fullstack Engineer&lt;/strong&gt;: someone who can write/fix "front end" and "back end" code, often a generalist given the necessity of context switching between so many frameworks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Backend Engineer&lt;/strong&gt;: specializing in APIs, Services, and often data storage (i.e. databases, files, etc.) - by contrast less focus directly on Users&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Web Engineer is a less common title as there is less need to distinguish from a Desktop Engineer =p&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;h4 id="software-engineering-gradations-by-years-of-experience"&gt;Software Engineering gradations by years of experience&lt;/h4&gt;
&lt;p&gt;These levels/titles are really fuzzy because the years may more directly relate to time in an organization or specifically to a framework/techstack.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Also, with talent and extreme focus/specialization someone within 2 years can be producing at the level of a "principal engineer"&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Junior&lt;/strong&gt; &lt;em&gt;(&amp;lt;2 years)&lt;/em&gt;: focused on implementation under supervision&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Senior&lt;/strong&gt; &lt;em&gt;(~between 2 and 5 years)&lt;/em&gt;: able to independently gather requirements and run a project from start to finish&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Principal&lt;/strong&gt; &lt;em&gt;(&amp;gt;5 years)&lt;/em&gt;: influence at the organizational level&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Generally it gets exponentially harder to "level up" &lt;em&gt;(it would be exceedingly rare to have an organization that is all principal engineers)&lt;/em&gt;. Interestingly, given the investment in time, training, and supervision required for junior engineers, it is also rare to have a "pyramid" shaped engineering organization.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;One caveat: a Software Engineer will apply best practices in creating solutions and systems (i.e. bigger picture complexity and scale), whereas a Programmer title hints at a more "limited to code" role.&lt;/em&gt;&lt;/p&gt;
&lt;h5 id="example-diagram-of-engineering-levels"&gt;Example Diagram of Engineering Levels&lt;/h5&gt;
&lt;p&gt;&lt;img alt="An example diagram of engineering career levels" src="../images/engineering-levels-layered-framework.png"/&gt;&lt;/p&gt;
&lt;h3 id="operations"&gt;Operations&lt;/h3&gt;
&lt;p&gt;Technology roles supporting the actual running of software and services...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SiteReliabilityEngineers (SRE)&lt;/strong&gt;: automating monitoring and stability as applied expertise via code &lt;em&gt;(&lt;a href="https://en.wikipedia.org/wiki/Site_Reliability_Engineering"&gt;https://en.wikipedia.org/wiki/Site_Reliability_Engineering&lt;/a&gt;)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DevOps&lt;/strong&gt;: automating the operations of systems and services (including monitoring, scripts and code writing, etc.) - leverages Dev skills with Ops experience &lt;em&gt;(&lt;a href="https://en.wikipedia.org/wiki/DevOps"&gt;https://en.wikipedia.org/wiki/DevOps&lt;/a&gt;)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Operations&lt;/strong&gt;: maintaining services/systems (hopefully still using automation and ideally transitioning to DevOps due to increased scale)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System Administrator&lt;/strong&gt;: manual job of maintaining a small number of (virtual or physical) machines/systems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;h3 id="data"&gt;Data&lt;/h3&gt;
&lt;p&gt;Information has to be stored somewhere, this specialization has become even more prevalent with the exponential growth of "big data"...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Data Scientist&lt;/strong&gt;: someone who uses mathematical tools like statistics, in conjunction with software and "big data", to answer questions &lt;em&gt;(or discover insights)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Engineer&lt;/strong&gt;: someone who builds infrastructure and tools that enables "Data Science", i.e. pipelines, warehouses&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DBA (Database Administrator)&lt;/strong&gt;: someone who manages the data for an organization, often an expert in the tooling and optimization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;h2 id="lateral-moves"&gt;Lateral Moves&lt;/h2&gt;
&lt;p&gt;Sometimes, besides all the levels and specializations, there are changes in career track &lt;em&gt;(significantly different responsibilities and focus)&lt;/em&gt; that are big enough to be called a "lateral move".&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Architect&lt;/strong&gt;: larger systems are inherently complex and designing and communicating the interfaces, especially across multiple teams/services, is an essential "big picture" role&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Having eng experiences leads to designs with fast and effective implementations and prevents "ivory tower"&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Engineering Manager&lt;/strong&gt;: people are non-deterministic, they cannot be debugged, and yet they are a part of every successful organization.
Someone who takes care of people, helps build and keep a team running smoothly, and achieves company outcomes. This is usually the role before Director and VP&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Having eng experience vastly increases credibility and the ability to estimate and deliver&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Quality&lt;/strong&gt;: someone who methodically thinks outside the box and regularly breaks boxes, ideally the most valuable boxes first&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Having eng experience means awareness of common shortcomings in frameworks/code and certain boundary conditions or real world scenarios (i.e. load)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt;: someone who thinks way outside of the box and finds ways to get inside of locked boxes&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Having eng experience allows for familiarity with architecture/framework/code flaws and automation of exploits&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Product Manager&lt;/strong&gt;: someone with passion and organizational skills who drives a product forward into the world&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Having eng experience allows for clearer and faster scope/timing discussions, and the ability to help the team with design or debugging&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Designer&lt;/strong&gt;: someone who loves the User and UI/UX and delivers highly desirable features and products&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Having eng experience allows for more effective collaboration and reduced time to market&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="other-important-aspects"&gt;Other Important Aspects&lt;/h2&gt;
&lt;p&gt;Career Development is not just getting the next position, it should also be about a lot of qualitative skills that lead to success:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Listening&lt;/strong&gt;: hearing Users, Stakeholders, Teammates, etc. is one of the most important parts of solving the right problems&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Communication&lt;/strong&gt;: proposing good solutions, asking hard questions, raising concerns, writing skills&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Time Management&lt;/strong&gt;: individual deadlines, team success&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interviewing&lt;/strong&gt;: being an interviewer is a completely different specialization - yet hiring cannot succeed without good technical interviewers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Business&lt;/strong&gt;: understanding impact and the consequences of outcomes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teamwork&lt;/strong&gt;: multiplying the efforts of those around you&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For each skill it is worth considering:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What is the individual's current level?&lt;/li&gt;
&lt;li&gt;How important is it to them to improve?&lt;/li&gt;
&lt;li&gt;What resources can be provided?&lt;/li&gt;
&lt;li&gt;How will they be encouraged and held accountable for progress?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Did I miss something? Are there missing nuances or outright incorrect statements?  Of course! =)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is no one-size-fits-all definition to this NP-Hard problem of "people and organizations over time"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="iterative-improvement-on-this-article"&gt;Iterative Improvement on this Article&lt;/h2&gt;
&lt;p&gt;My brash/blunt style is apparent in this article, but for a peek at how much better things get with collaboration and an editor check out the polished version:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://web.archive.org/web/20200415071712/https://blog.helix.com/helping-your-engineers-grow/"&gt;https://web.archive.org/web/20200415071712/https://blog.helix.com/helping-your-engineers-grow/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Also, I have later stumbled across this similar idea of "the range of software careers" done as a podcast back in 2008:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.se-radio.net/2008/09/episode-110-roles-in-software-engineering-i/"&gt;https://www.se-radio.net/2008/09/episode-110-roles-in-software-engineering-i/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.se-radio.net/2008/09/episode-112-roles-in-software-engineering-ii/"&gt;https://www.se-radio.net/2008/09/episode-112-roles-in-software-engineering-ii/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="leadership"/><category term="career"/><category term="engineering"/><category term="roles"/><category term="software"/><category term="management"/></entry><entry><title>Go FaaS with AWS Lambda</title><link href="https://blog.john-pfeiffer.com/go-faas-with-aws-lambda/" rel="alternate"/><published>2018-06-11T19:48:00-07:00</published><updated>2018-06-11T19:48:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2018-06-11:/go-faas-with-aws-lambda/</id><summary type="html">
&lt;p&gt;The promise of creating functions that do not require server administration is amazing,
the reality though includes a huge maze of vendor specific commands and frameworks (including permissions).&lt;/p&gt;
&lt;h2 id="why-function-as-a-service"&gt;Why Function as a Service&lt;/h2&gt;
&lt;p&gt;In many ways most of the work in software engineering is "accidental complexity". Deployment. Input/Output Parsing …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;The promise of creating functions that do not require server administration is amazing,
the reality though includes a huge maze of vendor specific commands and frameworks (including permissions).&lt;/p&gt;
&lt;h2 id="why-function-as-a-service"&gt;Why Function as a Service&lt;/h2&gt;
&lt;p&gt;In many ways most of the work in software engineering is "accidental complexity". Deployment. Input/Output Parsing. Monitoring. Logging. etc.&lt;/p&gt;
&lt;p&gt;The "web request" model conquered (much like the historical domination of SQL) in the 90's as networks and the "inter-network" became popular 
(overwhelming the fragmented and isolated vendor specific applications approaches).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nap.edu/read/6323/chapter/8#159"&gt;https://www.nap.edu/read/6323/chapter/8#159&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/SQL"&gt;https://en.wikipedia.org/wiki/SQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Web_application#History"&gt;https://en.wikipedia.org/wiki/Web_application#History&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#History"&gt;https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#History&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Yet what if your problem/data is not an input/output web request? (Yes of course there are GPUs and dedicated co-location centers...)&lt;/p&gt;
&lt;p&gt;Or what about loads of traffic that spike in very extreme bursts (and diminish to almost nothing)?&lt;/p&gt;
&lt;p&gt;The allure then is to have a function that runs on demand: truly elastic compute that does not require provisioning a server (not a virtual one and even without a web server).&lt;/p&gt;
&lt;p&gt;Without mangaing an OS (and all that security headache!) and especially not paying for idle resources but instead only getting/paying for lots of compute when needed.&lt;/p&gt;
&lt;p&gt;Of course there's no free lunch so as that complexity balloon gets squeezed it is the infrastructure/framework vendor that must "magically" provide the input, execute the function, and return the output.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The irony of "serverless" is that there's still a physical server, drivers, an operating system, and even a web framework, it's just someone else's (problem/revenue).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For "straightforward" web applications it may make more sense to directly offload the hosting/framework but still be in the same comfortable web server model (like Heroku)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/infrastructure-as-code-with-terraform-and-aws/#tools-to-manage-state-vs-platform-as-a-service"&gt;https://blog.john-pfeiffer.com/infrastructure-as-code-with-terraform-and-aws/#tools-to-manage-state-vs-platform-as-a-service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/go-web-development-and-templates-with-heroku"&gt;https://blog.john-pfeiffer.com/go-web-development-and-templates-with-heroku&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="source-code"&gt;Source Code&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"context"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"time"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"github.com/aws/aws-lambda-go/lambda"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// MyRequest demonstrates an input value&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyRequest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:"value"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// MyResponse helps illustrate how AWS Lambda auto&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyResponse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:"message"`&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:"created"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// HandleRequest https://docs.aws.amazon.com/lambda/latest/dg/go-programming-model-handler-types.html&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HandleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MyResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;UTC&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyResponse&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hi %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;Created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RFC3339&lt;/span&gt;&lt;span class="p"&gt;))},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HandleRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/compute/announcing-go-support-for-aws-lambda"&gt;https://aws.amazon.com/blogs/compute/announcing-go-support-for-aws-lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/go-programming-model-handler-types.html"&gt;https://docs.aws.amazon.com/lambda/latest/dg/go-programming-model-handler-types.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="aws-cli-deployment"&gt;AWS CLI Deployment&lt;/h2&gt;
&lt;p&gt;To avoid some of the complexity with the CLI you may want to first dip your toes in with the Web UI creation of a Go Lambda:
&lt;a href="https://us-west-1.console.aws.amazon.com/lambda/home?region=us-west-1#/create?firstrun=true"&gt;https://us-west-1.console.aws.amazon.com/lambda/home?region=us-west-1#/create?firstrun=true&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="packaging-for-upload"&gt;Packaging for Upload&lt;/h3&gt;
&lt;p&gt;The easy part is creating a binary...&lt;/p&gt;
&lt;p&gt;&lt;code&gt;go build -o examplebinary&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;build into a single binary&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;zip deployment.zip examplebinary&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;wrap it up in a zip&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;Installing the awscli may be a chore (if pip is broken), but &lt;code&gt;sudo apt install awscli&lt;/code&gt; (from 16.04 should be advanced enough)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Unable to locate credentials. You can configure credentials by running "aws configure".&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So in order to have credentials you must create a User in your AWS Account (with programmatic access only) which will generate an API key for you. (I suggest using the WebUI)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html"&gt;https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://console.aws.amazon.com/iam/home?region=us-west-1#/users"&gt;https://console.aws.amazon.com/iam/home?region=us-west-1#/users&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Do not use your "Root" Admin account in AWS for the API credentials, security best practice means creating at least one separate User&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="aws-permissions-and-roles"&gt;AWS Permissions and Roles&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;You may have to use the Web UI or some other mechanism to create the IAM role with the correct permissions&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Gotcha: the minimum permission would be "lambda:CreateFunction"&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/intro-permission-model.html#lambda-intro-execution-role"&gt;https://docs.aws.amazon.com/lambda/latest/dg/intro-permission-model.html#lambda-intro-execution-role&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://console.aws.amazon.com/iam/home?region=us-west-1#/policies"&gt;https://console.aws.amazon.com/iam/home?region=us-west-1#/policies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://console.aws.amazon.com/iam/home?region=us-west-1#/roles"&gt;https://console.aws.amazon.com/iam/home?region=us-west-1#/roles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This means the laborious process of creating a Policy that includes all the Lambda permissions (not very secure but it works)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;iam:PassRole&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hilariously the pre-created roles listed in the documentation do not have that specific IAM extra permission, somehow by default an Admin of the account should deploy lambdas?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "iam:PassRole",
                "lambda:*"
            ],
            "Resource": "*"
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="create-an-aws-user-with-api-credentials"&gt;Create an AWS user with API Credentials&lt;/h2&gt;
&lt;p&gt;So in order to have API credentials (~/.aws/credentials) you have already created:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A Policy&lt;/li&gt;
&lt;li&gt;A Role that can use that Policy&lt;/li&gt;
&lt;li&gt;A Group that has the Policy attached&lt;/li&gt;
&lt;li&gt;A User that has credentials (and is in the Group)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="the-actual-create-lambda-command"&gt;The Actual Create Lambda Command&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;aws&lt;span class="w"&gt; &lt;/span&gt;lambda&lt;span class="w"&gt; &lt;/span&gt;create-function&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
--region&lt;span class="w"&gt; &lt;/span&gt;us-west-1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
--function-name&lt;span class="w"&gt; &lt;/span&gt;ExampleFunction&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
--zip-file&lt;span class="w"&gt; &lt;/span&gt;fileb://./deployment.zip&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
--runtime&lt;span class="w"&gt; &lt;/span&gt;go1.x&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
--role&lt;span class="w"&gt; &lt;/span&gt;arn:aws:iam::1234YOURACCOUNT:role/lambda-all&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
--handler&lt;span class="w"&gt; &lt;/span&gt;examplebinary
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;fileb format = file binary&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The JSON response from creating the new lambda function (and uploading the zipped Go binary)...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{
    "LastModified": "2018-06-12T04:55:36.327+0000",
    "Version": "$LATEST",
    "FunctionArn": "arn:aws:lambda:us-west-1:1234YOURACCOUNT:function:ExampleFunction",
    "MemorySize": 128,
    "Runtime": "go1.x",
    "Role": "arn:aws:iam::1234YOURACCOUNT:role/lambda-all",
    "Description": "",
    "CodeSha256": "45R3BZKesxMM3AuZ96lS9UoiOEGX964oHD/J8QQfLfQ=",
    "Timeout": 3,
    "FunctionName": "ExampleFunction",
    "Handler": "examplebinary",
    "CodeSize": 2793060
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html"&gt;https://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="trigger-a-lambda-manually"&gt;Trigger a Lambda Manually&lt;/h2&gt;
&lt;p&gt;Using the WebUI (AWS Console) you can select the function created with the CLI
&lt;a href="https://us-west-1.console.aws.amazon.com/lambda/home?region=us-west-1#/functions"&gt;https://us-west-1.console.aws.amazon.com/lambda/home?region=us-west-1#/functions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;By using the "Configure test events" (sometimes a dropdown on the right next to the Test button)&lt;/p&gt;
&lt;p&gt;You can create a new "test event" , in this case a MyRequest (though any arbitrary extraneous JSON will be ignored)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"key2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"key1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;this test event sends one value and some extraneous JSON keys as input to the lambda function&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hi john"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2018-06-12T04:34:22Z"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the lambda function output is automatically converted from an object to JSON&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note that the WebUI also provides a way to upload a newer definition of the lambda function (zipped in a file)&lt;/p&gt;
&lt;h2 id="lambda-cron-aka-cloudwatch-scheduled-events"&gt;Lambda Cron aka CloudWatch Scheduled Events&lt;/h2&gt;
&lt;p&gt;The real power of AWS Lambdas involves how it becomes glue for connecting lots of other AWS services (and transformations).&lt;/p&gt;
&lt;p&gt;For example CloudWatch can be used to schedule/trigger a lambda.&lt;/p&gt;
&lt;p&gt;In the WebUI you can add a trigger via the Designer (on the left)&lt;/p&gt;
&lt;p&gt;Select "CloudWatch Events" -&amp;gt; Configure triggers (scroll down) -&amp;gt; Create a new rule&lt;/p&gt;
&lt;p&gt;Configure a trigger, "Scheduled" (as opposed to Rate which is similar but different ;)&lt;/p&gt;
&lt;p&gt;Name: "examplecron"&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cron(16 * ? * MON-FRI *)&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This would be on the 16th minute every hour every day every month on Monday through Friday (every year)
cron(Minutes Hours Day-of-month Month Day-of-week Year)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Disabling the "example" Cloud Watch Event is as easy as toggling a radio button
(this is an interesting way of controlling how/when your function executes)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html"&gt;https://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="lambda-monitoring"&gt;Lambda Monitoring&lt;/h2&gt;
&lt;p&gt;In the same WebUI where you view the Lambda function (by name) you can click on Monitoring and click around to see Metrics
(i.e. Lambda Cron aka CloudWatch Scheduled Events) actually "invoked" your function.&lt;/p&gt;
&lt;p&gt;Of course if you haven't enabled CloudWatch Logging or more importantly created a "Log Group" then you get nothing.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html"&gt;https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="more-info-on-lambdas"&gt;More Info on Lambdas&lt;/h2&gt;
&lt;p&gt;Here's a full source code example: &lt;a href="https://github.com/johnpfeiffer/aws-go-lambda"&gt;https://github.com/johnpfeiffer/aws-go-lambda&lt;/a&gt;
Compare the unit tests to those from a full web server handler: &lt;a href="https://github.com/johnpfeiffer/go-web-example/blob/master/controller_test.go"&gt;https://github.com/johnpfeiffer/go-web-example/blob/master/controller_test.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Apparently in order to test the tight integration with using AWS Lambda to consume events from other AWS Services there's a tool:
&lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/08/introducing-aws-sam-local-a-cli-tool-to-test-aws-lambda-functions-locally"&gt;https://aws.amazon.com/about-aws/whats-new/2017/08/introducing-aws-sam-local-a-cli-tool-to-test-aws-lambda-functions-locally&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="api-gateway"&gt;API Gateway&lt;/h2&gt;
&lt;p&gt;As a pre-requisite (and to leverage the fully integrated nature of the AWS ecosystem) we will create some data first...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And of course create a new Role that is linked to a Policy =(&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://console.aws.amazon.com/iam/home?region=us-west-1#/roles"&gt;https://console.aws.amazon.com/iam/home?region=us-west-1#/roles&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ensure you have a new Role (confusingly in the UI click through as a Lambda Service needing access) and choose the predefined Policy "AmazonDynamoDBFullAccess"&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;123476797434&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;DynamoDBFull&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Of course you'll have your own unique ARN and Security best practice would involve defining a more precise role (i.e. not giving permission for Auto Scaling)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Modify whom can assume the Role (using the AWS Web UI Console) &lt;a href="https://console.aws.amazon.com/iam/home?region=us-west-1#/roles/DynamoDBFull?section=trust"&gt;https://console.aws.amazon.com/iam/home?region=us-west-1#/roles/DynamoDBFull?section=trust&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you receive an error later on it will be because of the Edit Trust Relationship, "API Gateway does not have permission to assume the provided role"&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": ["apigateway.amazonaws.com", "lambda.amazonaws.com"]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Click on "Update Trust Policy"&lt;/p&gt;
&lt;h3 id="readonly-via-dynamodb"&gt;Readonly via DynamoDB&lt;/h3&gt;
&lt;p&gt;Use the WebUI (console) to create a new table &lt;a href="https://us-west-1.console.aws.amazon.com/dynamodb/home?region=us-west-1"&gt;https://us-west-1.console.aws.amazon.com/dynamodb/home?region=us-west-1&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a table named: stocks&lt;/li&gt;
&lt;li&gt;Primary Key: symbol (string)&lt;/li&gt;
&lt;li&gt;Sort Key: timestamp (number)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Uncheck Auto Scaling "Read capacity" and "Write capacity" checkboxes&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do not use the default settings, we will disable autoscaling in order to save money and somewhat arrive at fixed cost&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AutoScaling.Console.html#AutoScaling.Console.Modifying"&gt;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AutoScaling.Console.html#AutoScaling.Console.Modifying&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes"&gt;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;(Note that types are quite specific in DynamoDB, e.g. the binary "scalar" value can only contain up to 400KB of base64 encoded data)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;After a few minutes of initialization select the "Items" tab then the "Create item" button to manually create an entry:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://us-west-1.console.aws.amazon.com/dynamodb/home?region=us-west-1#tables:selected=stocks;tab=items"&gt;https://us-west-1.console.aws.amazon.com/dynamodb/home?region=us-west-1#tables:selected=stocks;tab=items&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the Web UI you will have to choose the dropdown (upper left) and change it from "Tree" to "Text" in order to enter a price&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VFINX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1529697600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;255.44&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Using a historical stock price (in dollars) at the close of business&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;(Adding a second entry for 1529611200 and 254.95 can be helpful too)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;After clicking Save and possibly a "Refresh data from server" (arrow lines in a circle on the right) you will see the new entries.&lt;/p&gt;
&lt;h4 id="setup-a-new-api-gateway-connected-to-dynamodb"&gt;Setup a new API Gateway connected to DynamoDB&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://console.aws.amazon.com/apigateway/home?region=us-west-1#/apis/create"&gt;https://console.aws.amazon.com/apigateway/home?region=us-west-1#/apis/create&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;"New API"&lt;/p&gt;
&lt;p&gt;API Name: stocks
Endpoint Type: regional&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Actions (Resource Actions) -&amp;gt; Create Resource&lt;/p&gt;
&lt;p&gt;Resource Name: stock
Resource Path: /{symbol}&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click "Create Resource" to save.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Actions (Resource Actions) -&amp;gt; Create Method (a dropdown appears in the WebUI below /{symbol}) , choose "GET"&lt;/p&gt;
&lt;p&gt;Integration type: AWS Service
Region: us-west-1
AWS Service: DynamoDB
HTTP Method: POST (for interactions with DynamoDB)
Action Type: Use action name
Action: GetItem
Execution role: arn:aws:iam::123476797434:role/DynamoDBFull
Content Handling: Passthrough&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;The permissions/role ARN was created earlier and GET using POST is actually for interacting with DynamoDB&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Click on the "Integration Request" for the /{symbol} - Get &lt;/li&gt;
&lt;li&gt;Scroll all the way to the bottom and expand "Body Mapping Templates" so that you can click on "Add mapping template"&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Content-Type: application/json&lt;/p&gt;
&lt;p&gt;Confusingly after you create your type you must click on it to get a UI to define the JSON transformation, use the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{
  "TableName": "stocks",
  "Key": {
    "symbol": {"S": "$input.params('symbol')"},
    "timestamp": {"N": "1529697600"}
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Emulating a direct low level request &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.LowLevelAPI.html"&gt;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.LowLevelAPI.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Back at the Web UI for "/{symbol} - GET - Method Execution" there is a button "TEST" that allows for validation of the API Gateway.&lt;/p&gt;
&lt;p&gt;This is helpful for detecting early the inevitable bugs that crop up.&lt;/p&gt;
&lt;p&gt;(Note: it does not seem to me that debugging an API this is way is actually faster...)&lt;/p&gt;
&lt;p&gt;In the Test UI enter the stock symbol "VFINX" underneath {symbol} to emulate a GET request, then click on the "Test" button.&lt;/p&gt;
&lt;p&gt;The Response Body on the right shows the response (that the client would see - though often revealing an error directly from DynamoDB)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Careful readers will notice that the timestamp variable is hardcoded in the transformation, todo: make the timestamp part of the query parameter"&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="where-is-the-code"&gt;Where is the Code?&lt;/h3&gt;
&lt;p&gt;One thing that is missing from this trivial example is how version control is applied to ensure deterministic change (and best practice reviews).&lt;/p&gt;
&lt;p&gt;Rather than just using the WebUI we should leverage "Infrastructure as Code" like CloudFormation but I would suggest more complete tools like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/docs/providers/aws/guides/serverless-with-aws-lambda-and-api-gateway.html"&gt;https://www.terraform.io/docs/providers/aws/guides/serverless-with-aws-lambda-and-api-gateway.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://serverless.com/framework/docs/providers/aws/events/apigateway/"&gt;https://serverless.com/framework/docs/providers/aws/events/apigateway/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One thing you might notice as you convert API Gateway + Lambda into code is that it begins to look a lot like code you would see in any web application,
except that it is in a DomainSpecificLanguage for a specific vendor framework.&lt;/p&gt;
&lt;h2 id="comparisons"&gt;Comparisons&lt;/h2&gt;
&lt;p&gt;Two things that are practically relevant are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Latency&lt;/li&gt;
&lt;li&gt;Cost&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lambdas will inherently have "warm up time" from a "cold start", while this can be mitigated (with a canary warmer etc.),
if your architecture is deliberately tying together lots of services over the network it may feel slow (and indeterministics).&lt;/p&gt;
&lt;p&gt;The contrast is to keep more things in memory or co-located in the same server. (Less network calls)&lt;/p&gt;
&lt;p&gt;Cost is fickle: Lambdas may be much cheaper but if configured incorrectly a DOS attack could create a lot of API Gateway events or CloudWatch logs (so indirect cost escalation).&lt;/p&gt;
&lt;p&gt;So there are workarounds but investing in them once again means the benefits (reduced overhead) come with a new complexity (and skill set).&lt;/p&gt;
&lt;p&gt;Google and Azure both provide functions as a service so while it may be possible to get price competition given the vendor lock-in nature switching costs may be non-trivial.&lt;/p&gt;
&lt;p&gt;Amazon cleverly have a "free tier" that covers enough to get developer hobby projects (aka time invested learning = biased professional purchasing choices) which covers everything in this article. &lt;a href="https://aws.amazon.com/free/"&gt;https://aws.amazon.com/free/&lt;/a&gt; &lt;em&gt;I have no affiliation with Amazon, Google, etc.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;From a philosophical perspective the choice is somewhat whether building with predefined vendor blocks (so WebUI or IaaC) is more valuable (time wise)/preferrable than code.&lt;/p&gt;
&lt;h2 id="trust"&gt;Trust&lt;/h2&gt;
&lt;p&gt;Something that underlies all of the AWS Lambda thinking and work is trust in the vendor.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Trust in their security practices&lt;/li&gt;
&lt;li&gt;Trust in their uptime and operations team&lt;/li&gt;
&lt;li&gt;Trust in their business (both longevity and pricing)&lt;/li&gt;
&lt;/ul&gt;</content><category term="build-CI-CD-devops"/><category term="go"/><category term="golang"/><category term="aws"/><category term="lambda"/><category term="cron"/><category term="iam"/><category term="cloudwatch"/><category term="dynamodb"/><category term="apigateway"/></entry><entry><title>Infrastructure as Code with Terraform and AWS</title><link href="https://blog.john-pfeiffer.com/infrastructure-as-code-with-terraform-and-aws/" rel="alternate"/><published>2018-05-12T07:35:00-07:00</published><updated>2018-05-12T07:35:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2018-05-12:/infrastructure-as-code-with-terraform-and-aws/</id><summary type="html">
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Just like building and maintaining hardware and physical servers by hand is manual drudge work (aka "toil"),
so is the manual deployment of servers and networking etc. even on "Infrastructure as a Service".&lt;/p&gt;
&lt;p&gt;The "Infrastructure as Code" movement emphasizes the value of human time and leverages source code and …&lt;/p&gt;</summary><content type="html">
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Just like building and maintaining hardware and physical servers by hand is manual drudge work (aka "toil"),
so is the manual deployment of servers and networking etc. even on "Infrastructure as a Service".&lt;/p&gt;
&lt;p&gt;The "Infrastructure as Code" movement emphasizes the value of human time and leverages source code and version control to manage the increasing complexity.
&lt;em&gt;(Think of the exponential growth of logical systems with virtualization, cloud services, containers, and micro-services etc.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Imagine a tool to automate deploying and managing virtual infrastructure...&lt;/p&gt;
&lt;p&gt;While AmazonWebServices CloudFormation works for setting up infrastructure via a config file, the user experience is non-optimal and there is definitely "vendor-lock-in".&lt;/p&gt;
&lt;p&gt;Hashicorp are a trusted brand in DevOps and having produced Vagrant, Packer, Consul, etc. it is easy to pick their Terraform product.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I have no affiliation with Hashicorp besides as a user of their software =)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While packer can build a single immutable image (an application or component) of a (virtual) server (or container),
Terraform is the higher level tool where the full setup including Load Balancer, Database, etc. are managed.
Terraform can also maintain "state" like replacing servers with (deploying) newer images that were built by packer.&lt;/p&gt;
&lt;p&gt;The configuration files can be applied to various Cloud and Infrastructure vendors providing some measure of portability.&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Infrastructure_as_Code"&gt;https://en.wikipedia.org/wiki/Infrastructure_as_Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/build-automation-using-packer-to-build-an-ami-use-immutable-not-chef"&gt;https://blog.john-pfeiffer.com/build-automation-using-packer-to-build-an-ami-use-immutable-not-chef&lt;/a&gt; (packer runs commands to create a machine image - aka a server frozen as a file)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/documentation/cloudformation"&gt;https://aws.amazon.com/documentation/cloudformation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-terraform-with-digitalocean"&gt;https://www.digitalocean.com/community/tutorials/how-to-use-terraform-with-digitalocean&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Basically you need access to the remote infrastructure, permissions to make changes (resources cost money!), and of course the Terraform tool.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download from &lt;a href="https://www.terraform.io/downloads.html"&gt;https://www.terraform.io/downloads.html&lt;/a&gt; (and probably unzip to \opt) the terraform binary, &lt;em&gt;you may also want to &lt;code&gt;echo "alias terraform='/opt/terraform'" &amp;gt;&amp;gt; ~/.bashrc&lt;/code&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Verify that the terraform binary has been installed and can execute: &lt;code&gt;terraform version ; terraform help&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create an SSH keypair: &lt;code&gt;ssh-keygen -t rsa -C "myemail@example.com" -f $HOME/.ssh/aws.id_rsa&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Upload the keypair to AWS &lt;a href="https://us-west-1.console.aws.amazon.com/ec2/v2/home?region=us-west-1#KeyPairs:sort=keyName"&gt;https://us-west-1.console.aws.amazon.com/ec2/v2/home?region=us-west-1#KeyPairs:sort=keyName&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Create a dedicated User with limited permissions: &lt;a href="https://console.aws.amazon.com/iam/home?region=us-west-1#/users"&gt;https://console.aws.amazon.com/iam/home?region=us-west-1#/users&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ensure the new User (i.e. terraform-demo) only has "Programmatic Access" (aka API only, not WebUI)&lt;/li&gt;
&lt;li&gt;Ensure the new User has permissions, i.e. is part of a Group (named ec2-only) that leverages the pre-generated policy name of EC2FullAccess)&lt;/li&gt;
&lt;li&gt;The last step of the create user wizard should show the ACCESS_KEY and SECRET_KEY , make sure you &lt;strong&gt;save these in a password manager&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Using Environment variables is a fairly standard and portable/secure way of providing credentials to Terraform, the &lt;strong&gt;region will determine where&lt;/strong&gt; resources are created&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"AKIAyourACCESSkey"&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"yourSECRETkeyABC123"&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-west-1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Alternatively save the credentials in the default amazon credentials file (~/.aws/credentials) by using &lt;code&gt;aws configure&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/docs/providers/aws/index.html"&gt;https://www.terraform.io/docs/providers/aws/index.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html"&gt;https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html"&gt;https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/global-infrastructure"&gt;https://aws.amazon.com/about-aws/global-infrastructure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="a-simple-build"&gt;A simple build&lt;/h2&gt;
&lt;p&gt;The simple and common example of deploying a new server instance from an AmazonMachineImage (with a new VPC for network control)&lt;/p&gt;
&lt;h3 id="example-terraform-file"&gt;Example Terraform File&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;example.tf&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;provider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;"aws"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-west-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kr"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;"aws_instance"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;"example"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;ami&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ami-dc2739bc"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;instance_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t2.nano"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;subnet_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${aws_subnet.us-west-1a-public.id}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kr"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;"aws_vpc"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;"example"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;cidr_block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.1.0/24"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;enable_dns_hostnames&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;enable_dns_support&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kr"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;"aws_subnet"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;"us-east-1a-public"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;vpc_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${aws_vpc.example.id}"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;cidr_block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.1.0/28"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;availability_zone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-west-1a"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The provider is the target (with a specific sub region), and each resource has a type and a name (which can be referenced in later variables)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;While Amazon Linux images are hardened CentOS with security updates it can be convenient to use Ubuntu 16.04 (and most likely real world use cases will be from an AMI you have built yourself)&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The AMI ID is region specific,  this example comes from us-west-1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud-images.ubuntu.com/locator/ec2"&gt;https://cloud-images.ubuntu.com/locator/ec2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html"&gt;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/docs/providers/aws/r/instance.html"&gt;https://www.terraform.io/docs/providers/aws/r/instance.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/instance-store-vs-ebs/"&gt;https://aws.amazon.com/premiumsupport/knowledge-center/instance-store-vs-ebs/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/virtualization_types.html"&gt;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/virtualization_types.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/default-vpc.html"&gt;https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/default-vpc.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Subnets.html"&gt;https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Subnets.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/free"&gt;https://aws.amazon.com/free&lt;/a&gt; &lt;em&gt;(not much is free)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="terraform-plan-and-apply"&gt;Terraform Plan and Apply&lt;/h3&gt;
&lt;p&gt;The single configuration file and the following commands will deploy the prescribed infrastructure to AWS.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;terraform init&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;install the AWS plugin (that is detected via the example.tf configuration file), you will need to git ignore .terraform and plugin binaries&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;terraform plan&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This will preview the steps that will occur&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;Terraform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;perform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;following&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;ami&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;"ami-dc2739bc"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"t2.nano"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;us&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;west&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                               &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;assign_ipv6_address_on_creation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;availability_zone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s"&gt;"us-west-1a"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                               &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;assign_generated_ipv6_cidr_block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;cidr_block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="s"&gt;"192.168.1.0/24"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;means created , - means removed , the VPC and subnet ids may even be populated&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;To avoid the issue of VPC or no VPC (and for better default security) this will explicitly create a new VPC.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;terraform apply&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Once again the expected results are displayed and a prompt requires confirmation&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;aws_instance.example: Creating...
  ami:                          "" =&amp;gt; "ami-44273924"
  ...
aws_instance.example: Still creating... (10s elapsed)
aws_instance.example: Creation complete after 16s (ID: i-0492ba9707e624a66)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the &lt;strong&gt;.terraform.tfstate&lt;/strong&gt; file contains the current state of the current infrastructure&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;terraform show&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A full listing of the details of the infrastructure is returned&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;To see the instance in the WebUI: &lt;a href="https://us-west-1.console.aws.amazon.com/ec2/v2/home?region=us-west-1#Instances:sort=instanceId"&gt;https://us-west-1.console.aws.amazon.com/ec2/v2/home?region=us-west-1#Instances:sort=instanceId&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Verify the new VPC &lt;a href="https://us-west-1.console.aws.amazon.com/vpc/home?region=us-west-1#vpcs:"&gt;https://us-west-1.console.aws.amazon.com/vpc/home?region=us-west-1#vpcs:&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="updates-to-existing-infrastructure-and-instances"&gt;Updates to Existing Infrastructure and Instances&lt;/h3&gt;
&lt;p&gt;If you modify and save example.tf file to add a tag Name:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;resource "aws_instance" "example" {
  tags {
    Name = "terraform-example"
  }
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;terraform plan ; terraform apply&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The remote infrastructure (server) has been updated with a tag/Name "example" , &lt;code&gt;terraform show&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you change the resource AMI ID (i.e. the base immutable image from which the server was created) then Terraform will destroy the old server and deploy a new one&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/intro/getting-started/change.html"&gt;https://www.terraform.io/intro/getting-started/change.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.gruntwork.io/an-introduction-to-terraform-f17df9c6d180"&gt;https://blog.gruntwork.io/an-introduction-to-terraform-f17df9c6d180&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="destroying"&gt;Destroying&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;terraform destroy&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a destructive command, as always you must type "yes" ... "There is no undo"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Terraform will perform the following actions:
  &lt;span class="k"&gt;-&lt;/span&gt; aws_instance.example
  &lt;span class="k"&gt;-&lt;/span&gt; aws_subnet.us-west-1a-public
  &lt;span class="k"&gt;-&lt;/span&gt; aws_vpc.example
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;terraform show&lt;/code&gt; will return nothing and inspection in the AWS Console (web ui) will show all the resources have been cleaned up =)&lt;/p&gt;
&lt;h2 id="a-load-balanced-example"&gt;A Load Balanced Example&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/intro/examples/aws.html"&gt;https://www.terraform.io/intro/examples/aws.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/terraform-providers/terraform-provider-aws/tree/master/examples/two-tier"&gt;https://github.com/terraform-providers/terraform-provider-aws/tree/master/examples/two-tier&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="tools-to-manage-state-vs-platform-as-a-service"&gt;Tools to Manage State vs Platform as a Service&lt;/h2&gt;
&lt;p&gt;Infrastructure as Code focuses on tools to manage complexity. An alternative is to outsource the infrastructure entirely by using something like a PlatformAsAService.&lt;/p&gt;
&lt;p&gt;Heroku (or Google AppEngine or Openshift) can simplify application deployment by reducing the input to simply the application code.&lt;/p&gt;
&lt;p&gt;The PaaS vendor manages the infrastructure (including load balancers) and can provide a WebUI and APIs for adding dependencies (i.e. databases).&lt;/p&gt;
&lt;p&gt;Though it may again require a vendor specific configuration file to specify which application is connected/has permissions to which database...&lt;/p&gt;</content><category term="build-CI-CD-devops"/><category term="terraform"/><category term="aws"/><category term="digital ocean"/><category term="devops"/><category term="immutable"/></entry><entry><title>Go Web Development and Templates with Heroku</title><link href="https://blog.john-pfeiffer.com/go-web-development-and-templates-with-heroku/" rel="alternate"/><published>2018-05-01T20:37:00-07:00</published><updated>2018-05-01T20:37:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2018-05-01:/go-web-development-and-templates-with-heroku/</id><summary type="html">
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Please create a new version controlled source code repository using Github or Bitbucket or ?.&lt;/li&gt;
&lt;li&gt;Have Golang installed&lt;/li&gt;
&lt;li&gt;Download and install the official Go dependency tool "dep", &lt;a href="https://golang.github.io/dep/docs/installation.html"&gt;https://golang.github.io/dep/docs/installation.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="a-simple-http-server"&gt;A simple http server&lt;/h2&gt;
&lt;p&gt;Your source repository currently only needs a single file: &lt;strong&gt;main.go …&lt;/strong&gt;&lt;/p&gt;</summary><content type="html">
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Please create a new version controlled source code repository using Github or Bitbucket or ?.&lt;/li&gt;
&lt;li&gt;Have Golang installed&lt;/li&gt;
&lt;li&gt;Download and install the official Go dependency tool "dep", &lt;a href="https://golang.github.io/dep/docs/installation.html"&gt;https://golang.github.io/dep/docs/installation.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="a-simple-http-server"&gt;A simple http server&lt;/h2&gt;
&lt;p&gt;Your source repository currently only needs a single file: &lt;strong&gt;main.go&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"log"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"os"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nb"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hi"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getEnvOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"8080"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Listening on port"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getEnvOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;defaultValue&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LookupEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;another example of a trivial web server using only the standard library&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Verify it runs locally with: &lt;code&gt;go run main.go&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;your browser should see "hi" in http://localhost:8080 and use "control + c" to cancel and quit the command line web server application&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While previous examples used Google App Engine  &lt;a href="https://blog.john-pfeiffer.com/go-programming-intro-with-vs-code-and-arrays-slices-functions-and-testing/#a-simple-web-server"&gt;https://blog.john-pfeiffer.com/go-programming-intro-with-vs-code-and-arrays-slices-functions-and-testing/#a-simple-web-server&lt;/a&gt;, here we will leverage Heroku for deploying our web application.&lt;/p&gt;
&lt;h2 id="go-dependency-management"&gt;Go Dependency Management&lt;/h2&gt;
&lt;p&gt;First download and install the Go dependency tool: &lt;a href="https://github.com/golang/dep"&gt;https://github.com/golang/dep&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DIRECTORY/OF/WEBAPP
dep&lt;span class="w"&gt; &lt;/span&gt;init
go&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[metadata.heroku]"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;Gopkg.toml
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"  root-package = \"`go list -e .`\""&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;Gopkg.toml
git&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;diff
git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;--all&lt;span class="w"&gt; &lt;/span&gt;.
git&lt;span class="w"&gt; &lt;/span&gt;commit&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"initial web app and using go dep"&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;An example simple Go dep configuration file &lt;a href="https://github.com/johnpfeiffer/web-go/blob/master/Gopkg.toml"&gt;https://github.com/johnpfeiffer/web-go/blob/master/Gopkg.toml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Further heroku metadata config options: &lt;a href="https://devcenter.heroku.com/articles/go-apps-with-dep#build-configuration"&gt;https://devcenter.heroku.com/articles/go-apps-with-dep#build-configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Similar heroku instructions on dep: &lt;a href="https://devcenter.heroku.com/articles/go-apps-with-dep#getting-started"&gt;https://devcenter.heroku.com/articles/go-apps-with-dep#getting-started&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="deploying-to-heroku-from-github-via-travis-ci"&gt;Deploying to Heroku from Github via Travis-CI&lt;/h2&gt;
&lt;h3 id="heroku-prerequisites"&gt;Heroku Prerequisites&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Download and install the heroku CLI &lt;a href="https://devcenter.heroku.com/articles/getting-started-with-go#set-up"&gt;https://devcenter.heroku.com/articles/getting-started-with-go#set-up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd /opt ; wget https://cli-assets.heroku.com/branches/stable/heroku-linux-amd64.tar.gz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tar xf heroku-linux-amd64.tar.gz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/opt/heroku/bin/heroku --version ; /opt/heroku/bin/heroku --help&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/opt/heroku/bin/heroku login&lt;/code&gt; &lt;em&gt;this will prompt for your email and password and store the credentials in ~/.netrc&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;For extra security consider using heroku CLI inside of an ephemeral docker container or a script that removes the credentials after each usage&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;These instructions will now assume heroku "just works", i.e. &lt;code&gt;alias heroku='/opt/heroku/bin/heroku'&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="configuring-the-application-with-heroku"&gt;Configuring the Application with Heroku&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DIRECTORY/OF/WEBAPP
heroku&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;
heroku&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;APPNAME&lt;span class="w"&gt; &lt;/span&gt;--buildpack&lt;span class="w"&gt; &lt;/span&gt;heroku/go
heroku&lt;span class="w"&gt; &lt;/span&gt;status
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This has created an empty Go application in Heroku&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Later on when deploying the application to heroku if you receive the following error: &lt;em&gt;No default language could be detected for this app&lt;/em&gt;, it means you did not set the buildpack language yet...&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you missed setting the buildpack language via CLI you can also use the WebUI after the app was already created:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dashboard.heroku.com/apps/APPNAME/settings"&gt;https://dashboard.heroku.com/apps/APPNAME/settings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcenter.heroku.com/articles/buildpacks"&gt;https://devcenter.heroku.com/articles/buildpacks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another gotcha is if you have not configured a dependency manager (even without dependencies!) you will see this error &lt;em&gt;App not compatible with buildpack&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id="travis-ci"&gt;Travis-CI&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.travis-ci.com/user/deployment/heroku/"&gt;https://docs.travis-ci.com/user/deployment/heroku/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First you will need to have retrieved your auth token: &lt;code&gt;heroku auth:token&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You can connect the repository that was already created in Github to Travis via &lt;a href="https://travis-ci.org"&gt;https://travis-ci.org&lt;/a&gt; , more specifically at &lt;a href="https://travis-ci.org/profile/USERNAME"&gt;https://travis-ci.org/profile/USERNAME&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;you may need to "sync account" if you very recently created a repository , otherwise you will see this error: &lt;em&gt;repository not known to https://api.travis-ci.org/: USERNAME/REPONAME&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-it&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;--volume&lt;span class="w"&gt; &lt;/span&gt;/absolute/path/repo:/opt/repo&lt;span class="w"&gt; &lt;/span&gt;--volume&lt;span class="w"&gt; &lt;/span&gt;/opt/heroku:/opt/heroku&lt;span class="w"&gt; &lt;/span&gt;ruby:alpine&lt;span class="w"&gt; &lt;/span&gt;/bin/sh
apk&lt;span class="w"&gt; &lt;/span&gt;update
apk&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;--no-cache&lt;span class="w"&gt; &lt;/span&gt;build-base&lt;span class="w"&gt; &lt;/span&gt;git
gem&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;travis&lt;span class="w"&gt; &lt;/span&gt;travis-lint
travis&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This docker example avoids installing ruby (or travis) locally ;p&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cd /opt/repo ; touch .travis.yml ; travis setup heroku&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;travis&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;heroku&lt;/span&gt;
&lt;span class="n"&gt;Heroku&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;************************************&lt;/span&gt;
&lt;span class="n"&gt;Heroku&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;Deploy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GITHUBUSERNAME&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;yes&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;Encrypt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;yes&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This simple CLI wizard prompts for the Heroku auth token and populates the .travis.yml with the encrypted value&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;If you want to do it manually you will need (using "vi" ;) to create a dummy .travis.yml file with the content:&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;deploy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;heroku&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;secure&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR ENCRYPTED API KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can then run the following command which will update the .travis.yml file with the real encrypted auth token.
&lt;code&gt;travis encrypt YOUR-HEROKU-TOKEN --add deploy.api_key -r GITHUBUSERNAME/REPONAME&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You may get prompted to login if you use the incorrect USERNAME&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In your source code repository your more complete .travis.yml file will be:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;
&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;
&lt;span class="n"&gt;notifications&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="n"&gt;deploy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;heroku&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;secure&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ABCD1234XYZLONGENCRYPTEDSTRING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;An interesting alternative to github and travis is bitbucket: &lt;a href="https://confluence.atlassian.com/bitbucket/deploy-to-heroku-872013667.html"&gt;https://confluence.atlassian.com/bitbucket/deploy-to-heroku-872013667.html&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confluence.atlassian.com/bitbucket/variables-in-pipelines-794502608.html"&gt;https://confluence.atlassian.com/bitbucket/variables-in-pipelines-794502608.html&lt;/a&gt; (where you enter your heroku API key as a variable)&lt;/li&gt;
&lt;li&gt;e.g. https://bitbucket.org/YOURUSERNAME/YOURREPONAME/addon/pipelines/deployments (on the far right there will be a gear symbol to configure variables)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that Using the Deployments UI (via adding the deployment: production line) will change the UI layout, and may require re-entering any Variables&lt;/p&gt;
&lt;p&gt;Also, use the Secured checkbox for any passwords or API keys when entered as a Variable&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bitbucket.org/atlassian/heroku-deploy/src/master/README.md"&gt;https://bitbucket.org/atlassian/heroku-deploy/src/master/README.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="bitbucket-pipelinesyml"&gt;bitbucket-pipelines.yml&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;golang&lt;/span&gt;
&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;full&lt;/span&gt;
&lt;span class="n"&gt;pipelines&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pipelines&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sh&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="n"&gt;IMPORT_PATH&lt;/span&gt;&lt;span class="o"&gt;}/&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;heroku&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;$HEROKU_API_KEY&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;heroku&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;$HEROKU_APP_NAME&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HEAD&lt;/span&gt;

&lt;span class="n"&gt;definitions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Try creating new credentials if you get an unauthorized error: Failed to create sources. Expected HTTP Status 200, got 401. Error message: {"id":"unauthorized","message":"Invalid credentials provided."}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;heroku authorizations:create&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You can see (and rename the description) of the Heroku tokens/api-keys listed at &lt;a href="https://dashboard.heroku.com/account/applications"&gt;https://dashboard.heroku.com/account/applications&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can input/update the token here: https://bitbucket.org/USERNAME/REPONAME/admin/addon/admin/pipelines/deployment-settings&lt;/p&gt;
&lt;h3 id="test-it"&gt;Test It&lt;/h3&gt;
&lt;p&gt;Now a browser that hits the Heroku URL will see "hi" , &lt;a href="https://web-go.herokuapp.com/"&gt;https://web-go.herokuapp.com/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="templates-for-content"&gt;Templates for Content&lt;/h2&gt;
&lt;p&gt;Separating out the static html content from dynamic and business logic parts of the application is a key way to remain modular.
Templates built into the Go standard library can provide output that is safe from code injection.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;indexTemplate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GetIndexTemplate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;indexTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;NoData&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;These small changes (var indexTemplate and indexTemplate.Execute) to the previous &lt;strong&gt;main.go&lt;/strong&gt; allows the default web handler (aka controller) to return html&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;indextemplate.go&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"html/template"&lt;/span&gt;

&lt;span class="c1"&gt;// NoData is an empty struct as I do not pass anything into the template&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;NoData&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// GetIndexTemplate returns the index.html template https://golang.org/pkg/html/template/#Template&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GetIndexTemplate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;indexTemplate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;style type="text/css"&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;body{&lt;/span&gt;
&lt;span class="s"&gt;  font-size: 1.9em;&lt;/span&gt;
&lt;span class="s"&gt;}&lt;/span&gt;
&lt;span class="s"&gt;&amp;lt;/style&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;hi&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;indexTemplate&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The function just returns the rendered template; since it is only called once in main it is not inefficient, and Must will panic if the template has an error&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;go run main.go indextemplate.go&lt;/code&gt; allows you to test it locally&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/html/template/"&gt;https://golang.org/pkg/html/template/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="passing-variables-to-a-template"&gt;Passing Variables to a Template&lt;/h3&gt;
&lt;p&gt;Dynamic, data driven web sites emphasize the power of software to create tables that nobody wants to write by hand.&lt;/p&gt;
&lt;p&gt;Building on the previous two examples (main.go, indextemplate.go) we now have a variation that passes data to the template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;hexcolors.go&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"html/template"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// HexColors wraps a list of colors as hexadecimal strings&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HexColors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Colors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// GetHexTemplate returns the parsed file as a template object&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GetHexTemplate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParseFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hexcolorstemplate.html"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hexController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16711680&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%06X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HexColors&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;hexTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;A handler/controller that generates hex color data&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;hexcolorstemplate.html&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  {{range .Colors}}
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{.}}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"background-color: {{.}}; "&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;__&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  {{end}}
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;range iterates and creates a table row for each color , (the html and body is elided, see the previous example)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For the full implementation see the web-go code example: &lt;a href="https://github.com/johnpfeiffer/web-go/commit/c8636ee4f54ff95d4a804a152954874f5c23b682"&gt;https://github.com/johnpfeiffer/web-go/commit/c8636ee4f54ff95d4a804a152954874f5c23b682&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;go run main.go indextemplate.go hexcolors.go&lt;/code&gt; allows you to test it locally, &lt;em&gt;note that you do not have to pass the html template as a parameter&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;More info:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/html/template/#Template.ParseFiles"&gt;https://golang.org/pkg/html/template/#Template.ParseFiles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.gopheracademy.com/advent-2017/using-go-templates/"&gt;https://blog.gopheracademy.com/advent-2017/using-go-templates/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="html-template-blocks-as-reusable-components"&gt;HTML Template Blocks as Reusable Components&lt;/h3&gt;
&lt;p&gt;Besides dynamic data and tables for "do not repeat yourself" there are also structural components that can be deduplicated.
Changes in a base html or css template (or common component definition) can therefore reliably be applied to a large number of files.&lt;/p&gt;
&lt;p&gt;Jinja2 is famous in Python for making it easier to work with websites, here are two different helpful mechanisms in Go:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use the keyword "define" to create a fragment that can be explicitly included&lt;/li&gt;
&lt;li&gt;Use the keyword "block" to create a default value that can be overridden&lt;blockquote&gt;
&lt;p&gt;the keyword "template" loads a template that has been created by a "define"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;indextemplate.go&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GetIndexTemplate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;indexTemplate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParseFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"base.tmpl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"index.html"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;indexTemplate&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This replaces the previous examples hardcoded html with a default base.tmpl that is then overridden by the "define" block in the index.html file, &lt;strong&gt;order matters&lt;/strong&gt;!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;base.tmpl&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/css"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;{block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"style"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Georgia"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.9&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;{end&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
{{block "content" .}}
{{end}}
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The HTML in base.tmpl has a "block" that provides a default style, content is an empty "block", &lt;em&gt;(clearly you can define variations of bases templates)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;index.html&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{{define "content"}}
hi , try &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/hexcolors"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;hexcolors&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
{{end}}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The tiny index file heavily leverages the default base template and uses "define" to only override the content block&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;go run main.go indextemplate.go hexcolors.go&lt;/code&gt; allows you to test it locally, &lt;em&gt;you do not pass the .tmpl nor .html template files as parameters&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Further illustration by extending the templates usage a little further:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;hexcolors.go&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GetHexTemplate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParseFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"base.tmpl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"components.tmpl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"hexcolorstemplate.html"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the template files must exist in the relative path and build in order&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;components.tmpl&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{{define "tablestyle"}}
  table, th, td {
    border: 2px solid black;
    font-size: 2.5em;
  }
{{end}}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;defining a specific fragment that can be used anywhere&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;hexcolorstemplate.html&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{{define "style"}}
{{template "tablestyle" .}}
{{end}}

{{define "content"}}
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    {{range .Colors}}
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{.}}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"background-color: {{.}}; "&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;__&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    {{end}}
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
{{end}}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the most complex example: the base template content is overridden with a table that gets data at runtime&lt;/p&gt;
&lt;p&gt;the style definition overrides the base template default style ; it actually gets populated by loading the tablestyle fragment definition&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;go run main.go indextemplate.go hexcolors.go&lt;/code&gt; allows you to test it locally, &lt;em&gt;you do not pass the .tmpl nor .html template files as parameters&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2 .tmpl files, 2 .html files, 3 .go files&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Full source code for this example: &lt;a href="https://github.com/johnpfeiffer/web-go/commit/b6da397f89c8a6955a30e665ff1aa99be989e5cb"&gt;https://github.com/johnpfeiffer/web-go/commit/b6da397f89c8a6955a30e665ff1aa99be989e5cb&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For further info and advanced features like cloning:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/golang/example/tree/master/template"&gt;https://github.com/golang/example/tree/master/template&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/html/template/#Template.Clone"&gt;https://golang.org/pkg/html/template/#Template.Clone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="testdrivendesign-and-gorilla-mux"&gt;TestDrivenDesign and Gorilla Mux&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Tests communicate. Use libraries and composition, not frameworks and magic&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Rather than re-invent the wheel it is useful to leverage well designed libraries to reduce bugs (and repetitive boilerplate code).&lt;/p&gt;
&lt;p&gt;Gorilla "mux" is a great idiomatic Go library for multi-plexing and routing. Designed to be modular to prefer composition we can also choose to leverage the Logging handler.&lt;/p&gt;
&lt;p&gt;Finally adding a bit of JSON and very concisely you have built a high performing API server.&lt;/p&gt;
&lt;p&gt;For the full featured source code with tests: &lt;strong&gt;&lt;a href="https://github.com/johnpfeiffer/go-web-example"&gt;https://github.com/johnpfeiffer/go-web-example&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/gorilla/mux"&gt;https://github.com/gorilla/mux&lt;/a&gt; in action &lt;a href="https://github.com/johnpfeiffer/go-web-example/blob/master/router.go"&gt;https://github.com/johnpfeiffer/go-web-example/blob/master/router.go&lt;/a&gt; &lt;em&gt;(with dependency injection for database testability)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/johnpfeiffer/time-go/blob/master/main.go"&gt;https://github.com/johnpfeiffer/time-go/blob/master/main.go&lt;/a&gt; leverages &lt;a href="https://github.com/gorilla/handlers#example"&gt;https://github.com/gorilla/handlers#example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/johnpfeiffer/go-web-example/blob/master/controller_test.go"&gt;https://github.com/johnpfeiffer/go-web-example/blob/master/controller_test.go&lt;/a&gt; has both unit and integration test examples&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TODO: integrating heroku postgres , &lt;a href="https://devcenter.heroku.com/articles/getting-started-with-go#use-a-database"&gt;https://devcenter.heroku.com/articles/getting-started-with-go#use-a-database&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="miscellaneous"&gt;Miscellaneous&lt;/h2&gt;
&lt;h3 id="how-to-return-specific-http-response-types"&gt;How to return specific HTTP Response types&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Note these code snippets are the Handler function only, you should provide your own HTTP listener and multi-plexer&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;headonly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"HEAD, GET"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WriteHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/GCfxTLdLGYn"&gt;https://play.golang.org/p/GCfxTLdLGYn&lt;/a&gt; example returning the HTTP Header where clearly the default status code is 200&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7"&gt;https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;curl --silent --write-out "%{http_code}" localhost:8080&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;httperror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Internal Server Error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusInternalServerError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// without the return statement execution would continue&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The http library understands that unexpected errors do occur but make sure to return so as to not continue executing code&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/FyB7FwVp-ZB"&gt;https://play.golang.org/p/FyB7FwVp-ZB&lt;/a&gt; example showing why the return statement is so important&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes"&gt;https://en.wikipedia.org/wiki/List_of_HTTP_status_codes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html"&gt;https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://curl.haxx.se/docs/manpage.html"&gt;https://curl.haxx.se/docs/manpage.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Serving binary files...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"images"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"example.png"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ServeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/G9zQ0SmzjP_K"&gt;https://play.golang.org/p/G9zQ0SmzjP_K&lt;/a&gt; example writing, reading, and serving a file&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/net/http/#ServeFile"&gt;https://golang.org/pkg/net/http/#ServeFile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/path/filepath/#Clean"&gt;https://golang.org/pkg/path/filepath/#Clean&lt;/a&gt; for sanitizing user input for loading file paths&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An example http server inside the Go playground: &lt;a href="https://play.golang.org/p/B-aZuQOdFtB"&gt;https://play.golang.org/p/B-aZuQOdFtB&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="logs-from-heroku"&gt;Logs from Heroku&lt;/h3&gt;
&lt;p&gt;To see the logs from the web application running in heroku: &lt;code&gt;heroku logs --app APPNAME --tail&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="environment-variables-for-configuration"&gt;Environment Variables for Configuration&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devcenter.heroku.com/articles/getting-started-with-go#define-config-vars"&gt;https://devcenter.heroku.com/articles/getting-started-with-go#define-config-vars&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="custom-domains-with-heroku"&gt;Custom Domains with Heroku&lt;/h3&gt;
&lt;p&gt;To use a custom domain name for your traffic &lt;a href="https://devcenter.heroku.com/articles/custom-domains"&gt;https://devcenter.heroku.com/articles/custom-domains&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="downloading-the-source-code-from-heroku"&gt;Downloading the source code from Heroku&lt;/h3&gt;
&lt;p&gt;To download the source code from a running Heroku application: &lt;code&gt;heroku git:clone --app APPNAME&lt;/code&gt; &lt;a href="https://devcenter.heroku.com/articles/git-clone-heroku-app"&gt;https://devcenter.heroku.com/articles/git-clone-heroku-app&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="using-https-for-a-go-web-server-with-a-self-signed-ssl-certficate"&gt;Using HTTPS for a Go Web Server with a Self Signed SSL Certficate&lt;/h2&gt;
&lt;h3 id="one-liner-to-generate-a-self-signed-certificate"&gt;One Liner to generate a self signed certificate&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;openssl req -subj '/CN=example.com/O=My Company Name LTD./C=US' -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout server.key -out server.crt&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="ssl-web-server-in-golang"&gt;SSL Web Server in Golang&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"log"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"os"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nb"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hi"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;certname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"server.crt"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;keyname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"server.key"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getEnvOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"8080"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Listening on port"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListenAndServeTLS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;certname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;keyname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getEnvOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;defaultValue&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LookupEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that self signed certificates will prompt a browser warning
&lt;code&gt;curl --insecure https://localhost:8080&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content><category term="programming"/><category term="go"/><category term="golang"/><category term="web"/><category term="gorilla mux"/><category term="http"/><category term="template"/><category term="heroku"/><category term="postgres"/></entry><entry><title>More Go Data Structures with Heap and Tree and Trie</title><link href="https://blog.john-pfeiffer.com/more-go-data-structures-with-heap-and-tree-and-trie/" rel="alternate"/><published>2018-02-10T07:07:00-08:00</published><updated>2018-02-10T07:07:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2018-02-10:/more-go-data-structures-with-heap-and-tree-and-trie/</id><summary type="html">
&lt;p&gt;Idiomatic Go depends heavily on the standard library.  Given that performance is often implementation dependent and directly related to the data being stored,
it makes sense that one must write some of the more common data structures from scratch.&lt;/p&gt;
&lt;p&gt;While slices (a.k.a. dynamic arrays) are included (as is …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Idiomatic Go depends heavily on the standard library.  Given that performance is often implementation dependent and directly related to the data being stored,
it makes sense that one must write some of the more common data structures from scratch.&lt;/p&gt;
&lt;p&gt;While slices (a.k.a. dynamic arrays) are included (as is a double linked list), a Set must be implemented using a map (for the specific type(s) of interest)
&lt;a href="https://blog.john-pfeiffer.com/golang-interfaces-stack-linked-list-queue-map-set/"&gt;https://blog.john-pfeiffer.com/golang-interfaces-stack-linked-list-queue-map-set/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Some of my favorite data structures are based upon trees...&lt;/p&gt;
&lt;h2 id="heap"&gt;Heap&lt;/h2&gt;
&lt;p&gt;A heap (&lt;a href="https://en.wikipedia.org/wiki/Heap_%28data_structure%29"&gt;https://en.wikipedia.org/wiki/Heap_%28data_structure%29&lt;/a&gt;) is a tree that maintains a specific property, i.e. the parent is always larger than its children
A binary heap (aka "priority queue" &lt;a href="https://en.wikipedia.org/wiki/Binary_heap"&gt;https://en.wikipedia.org/wiki/Binary_heap&lt;/a&gt;) has some very helpful and efficient qualities, especially when implemented by using an array (using computations leveraging the inherent relationship with powers of 2).
For instance, it is possible to use something like heapsort to sort an array in-place.&lt;/p&gt;
&lt;p&gt;A common use for a min-heap or max-heap is to be able to answer a query in O(1). (The top/first item retrieved is the answer!)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And after you get that answer, the next root item you "pop" is the next best answer!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://golang.org/pkg/container/heap/"&gt;https://golang.org/pkg/container/heap/&lt;/a&gt; is already included in the standard library =]&lt;/p&gt;
&lt;h2 id="binary-search-tree"&gt;Binary Search Tree&lt;/h2&gt;
&lt;p&gt;A binary search tree maintains a specific property between all nodes in the tree.  This is most often used to keep a collection of items sorted in order.&lt;/p&gt;
&lt;p&gt;If the tree is balanced (&lt;a href="https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree"&gt;https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree&lt;/a&gt;) we can mathematically guarantee the time for various operations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Because each branch splits into 2 it follows intuitively that time bounds are logn (the fancy way of saying the inverse of 2^x)&lt;/strong&gt; &lt;a href="https://en.wikipedia.org/wiki/Binary_logarithm"&gt;https://en.wikipedia.org/wiki/Binary_logarithm&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/johnpfeiffer/gotree"&gt;https://github.com/johnpfeiffer/gotree&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="trie"&gt;Trie&lt;/h2&gt;
&lt;p&gt;An interesting variation of a tree is a trie that allows for very fast retrieval of information (if it exists)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Trie"&gt;https://en.wikipedia.org/wiki/Trie&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A common example is quickly identifying if a string is available (i.e. autocompletion suggestions of words or names, or "boggle")
(Hence the obvious application of a trie also-known-as a "prefix tree") , video of Zelenski at Stanford explaining: &lt;a href="https://youtu.be/TJ8SkcUSdbU?t=13m21s"&gt;https://youtu.be/TJ8SkcUSdbU?t=13m21s&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/johnpfeiffer/gotree"&gt;https://github.com/johnpfeiffer/gotree&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="benefits"&gt;Benefits&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A trie is fast, like O(m) where m = the length of the search string (assuming that m is far smaller than all n keys in the tree)&lt;/li&gt;
&lt;li&gt;Unlike hashtables no hashing algorithm is required&lt;/li&gt;
&lt;li&gt;"intermediate" or "subset" solutions/queries are possible by returning a subtree&lt;/li&gt;
&lt;li&gt;Iterating through the trie can return the keys in a specific (e.g. alphabetical) order&lt;/li&gt;
&lt;/ul&gt;</content><category term="programming"/><category term="go"/><category term="golang"/><category term="heap"/><category term="tree"/><category term="bst"/><category term="trie"/></entry><entry><title>Working successfully with remote contractors</title><link href="https://blog.john-pfeiffer.com/working-successfully-with-remote-contractors/" rel="alternate"/><published>2018-01-20T21:14:00-08:00</published><updated>2018-01-20T21:14:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2018-01-20:/working-successfully-with-remote-contractors/</id><summary type="html">
&lt;h2 id="some-background"&gt;Some Background&lt;/h2&gt;
&lt;p&gt;It can be challenging to scale up an organization. One choice that can be made is "full time employee" versus "contractor". Another choice is "Co-Located" versus "Remote".&lt;/p&gt;
&lt;p&gt;There are some important considerations with any combination but I'll share my experiences with "Remote Contractors".&lt;/p&gt;
&lt;p&gt;I've worked with remote contractors …&lt;/p&gt;</summary><content type="html">
&lt;h2 id="some-background"&gt;Some Background&lt;/h2&gt;
&lt;p&gt;It can be challenging to scale up an organization. One choice that can be made is "full time employee" versus "contractor". Another choice is "Co-Located" versus "Remote".&lt;/p&gt;
&lt;p&gt;There are some important considerations with any combination but I'll share my experiences with "Remote Contractors".&lt;/p&gt;
&lt;p&gt;I've worked with remote contractors from Israel, Italy, England, Pakistan, and Ukraine. I've also personally worked as both an on-site contractor and a remote contractor.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A contractor is free to take on other client work in parallel or to say no to a project.  They may also decide to stop taking new contracts.&lt;/li&gt;
&lt;li&gt;A long term contractor is incentivized to deliver sustainable value for repeat business.&lt;/li&gt;
&lt;li&gt;A remote contractor might never be met in-person (instead only via video, chat, email, etc.) - they can be based anywhere in the world.&lt;/li&gt;
&lt;li&gt;Contractors typically cost less for a short duration (considering the full cost of HR and the interviewing process, employee benefits, cost of office space, etc.)&lt;/li&gt;
&lt;li&gt;Contractors tend to cost more over a longer duration (i.e. you are paying a premium for the overhead of their company administration, marketing, profit margin, etc. , though it still might be absolutely less than an employee)&lt;/li&gt;
&lt;li&gt;Contracts and payments can be tricky, especially across international borders, though technology and digital payments have made this a lot simpler.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="myths"&gt;Myths&lt;/h2&gt;
&lt;p&gt;The following are some of the many negative things I've heard:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Outsourcing is evil and consultants and contractors are the most expensive way to ruin your business =(&lt;/li&gt;
&lt;li&gt;Foreigners are hard to understand and work with&lt;/li&gt;
&lt;li&gt;You should only hire contractors for a specific task&lt;/li&gt;
&lt;li&gt;Remote workers are virtual and so they don't matter as much&lt;/li&gt;
&lt;li&gt;Contractors, especially remote ones, will never be as passionate and invested as your employees&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="a-story-with-a-happy-ending"&gt;A story with a happy ending&lt;/h2&gt;
&lt;p&gt;Of my many experiences I'll share a recent one where I've had fabulous success with some contractors in Ukraine.&lt;/p&gt;
&lt;p&gt;We started with three open positions and had a happy coincidence that a potential team lead contractor was in the Bay Area finishing a different assignment.
The in-person interview went well (communicated well, strong technical skills, showed ambition and experience with leading a team). I interviewed him just like I would any new employee/team member.&lt;/p&gt;
&lt;p&gt;In the next phase I immediately encouraged this first contractor, our remote team lead, to be engaged in the interviewing of the remaining two members. (After all, they'd be on his team!)
I then received resumes from each prospective candidate he'd already reviewed/interviewed.  Next I had a conference call with each candidate where again I focused on communication, experience, and ability.&lt;/p&gt;
&lt;p&gt;Once we got the team assembled we codified on a Monday weekly meeting that fit in the narrow window of time that accomodated the 10 hour timezone difference.
All other coordination would be through Confluence, Pull Requests, HipChat (chat and, and of course, if needed, video call).&lt;/p&gt;
&lt;p&gt;I gave them an example of the work I had done already for the task and asked about their experiences with different solutions/technology choices (test automation and Selenium).
I also worked with them to create an inventory of the test coverage. Each week we would discuss the current progress and if there was a change in priority (i.e. a critical security release requiring extra help with manual validation).&lt;/p&gt;
&lt;p&gt;After a year it became a clear success: productive part of the team, doubled their head count, and allowing us to ship with more confidence faster than ever before.
Some of the trust has been having them own their portion of the system (CI and Automated Testing), very often when they bring up a bug or the tests fail we have to listen: delegation means trusting.&lt;/p&gt;
&lt;p&gt;The timezone difference is still hard and they maintain their own Kanban board even though they participate in the common sprint planning meeting.
As they've learned our problem domain and culture they have grown to proactively solve problems (like a hackathon where they wrote an API library for testing, or their extension into performance testing, and many other additions).
We could not have hired as many full time employees so quickly nor with their specific expertise: our success is due to their success.&lt;/p&gt;
&lt;h2 id="things-ive-learned-to-succeed"&gt;Things I've learned to succeed&lt;/h2&gt;
&lt;p&gt;Especially: Ensure there is clear Communication&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have a regular meeting (I find weekly is the best cadence) to make sure progress is being made on the correct things and identify any "blockers"&lt;/li&gt;
&lt;li&gt;Prepare written agendas, repeat any decisions made in writing&lt;/li&gt;
&lt;li&gt;Verbally (Video Conference is preferred for visual cues) for "high bandwidth" back and forth, give them an opportunity to ask questions!&lt;/li&gt;
&lt;li&gt;Provide asynchronous tools to overcome timezone differences (HipChat/JIRA/Confluence)&lt;/li&gt;
&lt;li&gt;Have a clear idea of exactly what you want them to achieve (talk through your "definition of done")&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Specifically: People and Organization&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Be involved and engaged in the interview process - your people and team are the largest factor for success or failure&lt;/li&gt;
&lt;li&gt;Trust is earned over time and is the most valuable component (especially when remote)&lt;/li&gt;
&lt;li&gt;You must give trust to receive trust&lt;/li&gt;
&lt;li&gt;For hiring a remote team lead: prefer communication and then organization skills and finally technical skills - remember this person must overcome language/cultural barriers&lt;/li&gt;
&lt;li&gt;Setup a remote hierarchy: they need to be able to resolve questions and become unblocked quickly via a local escalation process (i.e. a single decision maker in their office/timezone)&lt;/li&gt;
&lt;li&gt;Don't be afraid to "lose a week" for them to learn something, research an approach, or just because of a misunderstanding (this is normal and it only feels more expensive than an "internal employee" because you actually get to see a bill for it)&lt;/li&gt;
&lt;li&gt;Provide open ended goals (don't micromanage and over-prescribe) and people will often exceed expectations&lt;/li&gt;
&lt;li&gt;Being nice is a universal truth&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="things-to-watch-out-for"&gt;Things to watch out for&lt;/h2&gt;
&lt;p&gt;Clearly not everything leads to success, here are some important considerations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Things that materially affect your user count or bottom line may need a stronger compliance/auditing chain than contracting firms can provide&lt;/li&gt;
&lt;li&gt;Misunderstandings can turn into personal conflicts if not managed correctly&lt;/li&gt;
&lt;li&gt;Beware slipping into an "us and them" mentality or "throwing it over the wall" or "finger pointing"&lt;/li&gt;
&lt;li&gt;Skills may not always match up and it's rare remote contractors will directly interact with your customers - don't create unwinnable situations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Be careful when using contractors for critical path projects or key competitive intellectual property:
- You are betting your project/company success on a group that has more external risk and less alignment
- They may be pressured into taking shortcuts and are less likely to have to "pay" the long term costs (e.g. technical debt by skipping tests or large volumes of support tickets)
- Slippage and mistakes happen, by keeping it inside your own organization these can be managed quickly and appropriately&lt;/p&gt;
&lt;h2 id="things-that-you-can-takeaway"&gt;Things that you can takeaway&lt;/h2&gt;
&lt;p&gt;A foreign contractor can be just a productive as a domestic contractor or employee despite language, geography, cultural, calendar, and timezone differences.
A good contractor is like finding a needle in a very large haystack so put in the effort to keep them happy.&lt;/p&gt;
&lt;p&gt;Contractors are people:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Personalize the relationship because you should get to know them: their interests, strengths, weaknesses, etc.&lt;/li&gt;
&lt;li&gt;Find how they complement your team and give them a path to grow as professionals&lt;/li&gt;
&lt;li&gt;Respect that they have national/religious holidays and personal lives&lt;/li&gt;
&lt;li&gt;Look to align their interests with yours, there has got to be more than "it's just about the money"&lt;/li&gt;
&lt;/ul&gt;</content><category term="leadership"/><category term="organization"/><category term="scaling"/><category term="people"/><category term="leading"/><category term="management"/><category term="contractors"/></entry><entry><title>Golang Buffalo Tutorial To Create A Web Site With Authentication</title><link href="https://blog.john-pfeiffer.com/golang-buffalo-tutorial-to-create-a-web-site-with-authentication/" rel="alternate"/><published>2017-12-02T19:44:00-08:00</published><updated>2017-12-02T19:44:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2017-12-02:/golang-buffalo-tutorial-to-create-a-web-site-with-authentication/</id><summary type="html">
&lt;p&gt;Go's considerable tooling makes it a very productive and performant static language. Besides being ideal for developing APIs (goroutines!) it can then be convenient to also write the Web UI in Go.&lt;/p&gt;
&lt;p&gt;Although the standard library allows for writing a Go based website with templates and maximum flexibility, what if …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Go's considerable tooling makes it a very productive and performant static language. Besides being ideal for developing APIs (goroutines!) it can then be convenient to also write the Web UI in Go.&lt;/p&gt;
&lt;p&gt;Although the standard library allows for writing a Go based website with templates and maximum flexibility, what if you want a quick start with "batteries included"?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Code generation makes it easy to get started but all future customization depends on the developer, this is not a CMS =p&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="buffalo-installation-and-prerequisites"&gt;Buffalo Installation and Prerequisites&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Assuming the Go dev environment is already setup...&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;go get -u -v github.com/gobuffalo/buffalo/buffalo&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;buffalo new example&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo docker run --rm -it --publish 0.0.0.0:5432:5432 --name pg -e POSTGRES_PASSWORD=postgres postgres:alpine&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;this will make the username and password match the default database.yml that buffalo generates...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;cat database.yml&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;development&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;postgres&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;example_development&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;postgres&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;postgres&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;127.0.0.1&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;5&lt;/span&gt;

&lt;span class="nt"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;envOr "TEST_DATABASE_URL" "postgres&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;//postgres&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;postgres@127.0.0.1&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;5432/example_test&lt;/span&gt;&lt;span class="p p-Indicator"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;sslmode=disable"&lt;/span&gt;&lt;span class="p p-Indicator"&gt;}}&lt;/span&gt;

&lt;span class="nt"&gt;production&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;envOr "DATABASE_URL" "postgres&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;//postgres&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;postgres@127.0.0.1&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;5432/example_production&lt;/span&gt;&lt;span class="p p-Indicator"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;sslmode=disable"&lt;/span&gt;&lt;span class="p p-Indicator"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;buffalo db create --all&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the buffalo framework will create all the necessary databases and tables&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;(you can also &lt;code&gt;buffalo db drop --all&lt;/code&gt; or &lt;code&gt;buffalo db drop --env test&lt;/code&gt; to remove all or just one db)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;references:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/postgres/"&gt;https://hub.docker.com/_/postgres/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gobuffalo.io/docs/db"&gt;https://gobuffalo.io/docs/db&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is worth reading about the convention of how Buffalo lays out the directories and code &lt;a href="https://gobuffalo.io/docs/directory-structure"&gt;https://gobuffalo.io/docs/directory-structure&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="start-developing-with-a-resource-generator"&gt;Start Developing with a Resource Generator&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;buffalo dev&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;(Or use the following syntax for a non default port: &lt;code&gt;PORT=3001 buffalo dev&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl localhost:3000&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Yup a default web page is routed and served&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;buffalo&lt;span class="w"&gt; &lt;/span&gt;generate&lt;span class="w"&gt; &lt;/span&gt;resource&lt;span class="w"&gt; &lt;/span&gt;users&lt;span class="w"&gt; &lt;/span&gt;name&lt;span class="w"&gt; &lt;/span&gt;email&lt;span class="w"&gt; &lt;/span&gt;title:nulls.Text
buffalo&lt;span class="w"&gt; &lt;/span&gt;db&lt;span class="w"&gt; &lt;/span&gt;migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Resulting output...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;--&amp;gt; actions/users.go
--&amp;gt; actions/users_test.go
--&amp;gt; locales/users.en-us.yaml
--&amp;gt; templates/users/_form.html
--&amp;gt; templates/users/edit.html
--&amp;gt; templates/users/index.html
--&amp;gt; templates/users/new.html
--&amp;gt; templates/users/show.html
--&amp;gt; buffalo db g model user name email title:nulls.Text
v3.41.1

--&amp;gt; models/user.go
--&amp;gt; models/user_test.go
--&amp;gt; goimports -w actions/actions_test.go actions/app.go actions/home.go actions/home_test.go actions/render.go actions/users.go actions/users_test.go grifts/db.go grifts/init.go main.go models/models.go models/models_test.go models/user.go models/user_test.go
&amp;gt; migrations/20171203042126_create_users.up.fizz
&amp;gt; migrations/20171203042126_create_users.down.fizz
--&amp;gt; goimports -w actions/actions_test.go actions/app.go actions/home.go actions/home_test.go actions/render.go actions/users.go actions/users_test.go grifts/db.go grifts/init.go main.go models/models.go models/models_test.go models/user.go models/user_test.go
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The code generator is very helpful, especially after the database tables are updated with the migrate command&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;curl localhost:3000&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The list of REST resources now includes all the usual HTTP methods&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;curl localhost:3000/users&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The GET endpoint lists all of the existing users (in the database) and has a button to create a new user&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now you can use a web browser to play with the Web UI&lt;/p&gt;
&lt;p&gt;You can also query the database&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;postgres&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;psql&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;postgres&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;psql&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;postgres&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;postgres&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nt"&gt;list&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nt"&gt;connect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;example_development&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nt"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nt"&gt;d&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;users&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;users&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="avoiding-the-frontend"&gt;Avoiding the Frontend&lt;/h2&gt;
&lt;p&gt;Because javascript moves so quickly (and breaks things) it is easier to skip these steps and focus on the backend.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# maybe skip these (and all the accompanying dependencies) to avoid suckiness
sudo apt-get install -y npm
sudo npm cache clean -f
sudo npm install -g n
npm --version
node --version
npm install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;npm installs the authrecipe dependencies but first use npm in order to install nodejs via the "n" helper&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Without these the javascript assets or other things that were supposed to make the forms look pretty are not available but everything still works.&lt;/p&gt;
&lt;h2 id="authentication"&gt;Authentication&lt;/h2&gt;
&lt;p&gt;One of the first building blocks of any decent site is authentication.  Luckily there are a couple of packages that make basic and SSO authentication easier.&lt;/p&gt;
&lt;h3 id="basic-authentication-with-the-database"&gt;Basic Authentication with the Database&lt;/h3&gt;
&lt;p&gt;To just use an example (leverage the Buffalo author's in-progress tutorial ;)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0.0.0&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;alpine&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gobuffalo&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;authrecipe&lt;/span&gt;
&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;authrecipe&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;
&lt;span class="n"&gt;buffalo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="ow"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;buffalo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;migrate&lt;/span&gt;
&lt;span class="n"&gt;buffalo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;that is the minimum to get up and runnning...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With a browser you can "sign in" or "register" (create a new user with a password)&lt;/p&gt;
&lt;p&gt;TODO: more work to port over how to create secure endpoints with a "CheckAuth" middleware...&lt;/p&gt;
&lt;h3 id="authentication-using-an-external-identity-provider"&gt;Authentication using an External Identity Provider&lt;/h3&gt;
&lt;p&gt;For instructions on how to have users authenticate and secure resources with identity providers like Github, Facebook, Google, etc.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0.0.0&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;alpine&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gobuffalo&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buffalo&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buffalo&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buffalo&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;goth&lt;/span&gt;
&lt;span class="n"&gt;buffalo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;
&lt;span class="n"&gt;buffalo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;goth&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In order to set up the Bitbucket OAuth credentials:
&lt;em&gt;Log into bitbucket -&amp;gt; from your user profile (avatar) dropdown choose "bitbucket settings" -&amp;gt; click on OAuth (on the left) -&amp;gt; Add consumer&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;CallbackURL (for development): http://127.0.0.1:3000/auth/bitbucket
Permissions: account email&lt;/p&gt;
&lt;p&gt;Now you have a KEY and SECRET&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html"&gt;https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.gobuffalo.io/buffalo-tutorial-create-a-site-with-github-auth-629582e2763e"&gt;https://blog.gobuffalo.io/buffalo-tutorial-create-a-site-with-github-auth-629582e2763e&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/markbates/goth"&gt;https://github.com/markbates/goth&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;BITBUCKET_KEY=foobar BITBUCKET_SECRET=barfoo buffalo dev&lt;/code&gt;&lt;/p&gt;</content><category term="programming"/><category term="go"/><category term="golang"/><category term="web"/><category term="buffalo"/><category term="authentication"/><category term="sso"/><category term="saml"/></entry><entry><title>Dynamic Programming and Memoization and the Compute versus Storage Tradeoff</title><link href="https://blog.john-pfeiffer.com/dynamic-programming-and-memoization-and-the-compute-versus-storage-tradeoff/" rel="alternate"/><published>2017-11-06T20:45:00-08:00</published><updated>2017-11-06T20:45:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2017-11-06:/dynamic-programming-and-memoization-and-the-compute-versus-storage-tradeoff/</id><summary type="html">
&lt;h2 id="tradeoffs"&gt;Tradeoffs&lt;/h2&gt;
&lt;p&gt;There is an inevitable tradeoff of storage versus computation speed, or space used versus time to run, in every program.&lt;/p&gt;
&lt;p&gt;Caching is often viewed as a performance optimization but sometimes it is the only way to achieve a result in a finite amount of time.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Dynamic_programming"&gt;https://en.wikipedia.org …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">
&lt;h2 id="tradeoffs"&gt;Tradeoffs&lt;/h2&gt;
&lt;p&gt;There is an inevitable tradeoff of storage versus computation speed, or space used versus time to run, in every program.&lt;/p&gt;
&lt;p&gt;Caching is often viewed as a performance optimization but sometimes it is the only way to achieve a result in a finite amount of time.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Dynamic_programming"&gt;https://en.wikipedia.org/wiki/Dynamic_programming&lt;/a&gt; is about breaking down a problem into sub-problems where each solution can be stored so that it can be looked up rather than recomputed.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Memoization"&gt;https://en.wikipedia.org/wiki/Memoization&lt;/a&gt; is related in that it is about storing the output of a function call when specific parameters are provided.
Thus it is often conceptualized as a special kind of caching.&lt;/p&gt;
&lt;h2 id="naive-fibonacci-recursion"&gt;Naive Fibonacci Recursion&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Fibonacci_number"&gt;https://en.wikipedia.org/wiki/Fibonacci_number&lt;/a&gt; is a classic numerical series where each subsequent number is the sum of the previous two numbers: &lt;code&gt;1 1 2 3 5 8 13...&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;An interesting problem is to calculate the series up to a certain point.  The straightforward solution uses recursion:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fibSeries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fibSeries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;go run main.go&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Each iteration in the series discards the previous results and then re-calculates the intermediate steps for each subsequent iteration&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="fibonacci-timings-and-golang-playground-time-is-frozen"&gt;Fibonacci timings and golang playground time is frozen&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://play.golang.org/p/esejwsN0lQ"&gt;https://play.golang.org/p/esejwsN0lQ&lt;/a&gt; is an example with timings but...
...sorry, the Go Playground does not really use time (and caches all output) so to really see the difference you must run the program locally (Andrew Gerrand explains)...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://groups.google.com/forum/#!topic/golang-nuts/Dh0P1VzXmq8&lt;/li&gt;
&lt;li&gt;https://groups.google.com/forum/#!topic/golang-nuts/NLZJahiMk58&lt;/li&gt;
&lt;li&gt;https://github.com/golang/playground/blob/master/sandbox/play.go&lt;/li&gt;
&lt;li&gt;http://www.gophercon.in/blog/2015/02/17/andrew/&lt;/li&gt;
&lt;li&gt;https://talks.golang.org/2014/go4gophers.slide#3&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="memoization-aka-caching-with-fibonacci"&gt;Memoization aka Caching with Fibonacci&lt;/h2&gt;
&lt;p&gt;It almost seems common sense that we should not be re-calculating answers that we already know for every step...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fibSeriesMemoization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fibMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fibMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fibMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fibMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Using a map as a lookup table caches the result of each function call during each iteration&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://play.golang.org/p/Wxgl_OwkTY"&gt;https://play.golang.org/p/Wxgl_OwkTY&lt;/a&gt; again provides the full code (despite the Go playground not really providing time elapsed)&lt;/p&gt;
&lt;p&gt;That's it: identifying the extra work and storing it somewhere that can be referenced, the trade-off is now that more memory is required. =]&lt;/p&gt;
&lt;h2 id="dynamic-programming-with-fibonacci-numbers"&gt;Dynamic Programming with Fibonacci Numbers&lt;/h2&gt;
&lt;p&gt;This alternative implementation removes the recursion (and helper function) and instead uses a simple for loop and a slice of ints.
It highlights the nuance of how Dynamic Programming is not necessarily just storing the result of a function call but genuinely understanding the nature of the problem.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fibDynamic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;this trivial example also benefits from the slice index corresponding well to a key for each fibonacci value in the "lookup table"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The example Dynamic Programming solution avoids the map lookup and so should be the most performant &lt;a href="https://play.golang.org/p/VY9ul6ievC"&gt;https://play.golang.org/p/VY9ul6ievC&lt;/a&gt;, but since the Go Playground time elapsed does not work...&lt;/p&gt;
&lt;h2 id="comparing-with-benchmarks"&gt;Comparing with Benchmarks&lt;/h2&gt;
&lt;p&gt;Besides the "manual performance testing" with time and print statements you can use Go's more sophisticated tooling with bench.&lt;/p&gt;
&lt;p&gt;Create &lt;strong&gt;main_test.go&lt;/strong&gt; and run &lt;code&gt;go test -v -run=NOMATCH -bench=BenchmarkFibonacciSeries&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;BenchmarkFibonacciSeriesRecursive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fibSeriesRecursive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;BenchmarkFibonacciSeriesMemoization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fibSeriesMemoization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;BenchmarkFibonacciSeriesDynamicProgramming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fibDynamic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The nanoseconds per operation are dramatically less in the side-by-side comparison&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;goos&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;linux&lt;/span&gt;
&lt;span class="n"&gt;goarch&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amd64&lt;/span&gt;
&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/johnpfeiffer/gosrc/&lt;/span&gt;&lt;span class="n"&gt;benchmarking&lt;/span&gt;
&lt;span class="n"&gt;BenchmarkFibonacciSeriesRecursive&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="mi"&gt;191632&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;
&lt;span class="n"&gt;BenchmarkFibonacciSeriesMemoization&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;200000&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;13675&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;
&lt;span class="n"&gt;BenchmarkFibonacciSeriesDynamicProgramming&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;2000000&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="mi"&gt;814&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;
&lt;span class="n"&gt;PASS&lt;/span&gt;
&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;org&lt;/span&gt;&lt;span class="sr"&gt;/johnpfeiffer/gosrc/&lt;/span&gt;&lt;span class="n"&gt;benchmarking&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;7.157&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/johnpfeiffer/go-fibonacci"&gt;https://github.com/johnpfeiffer/go-fibonacci&lt;/a&gt; for full source code&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Reference for running go benchmarking &lt;a href="https://blog.john-pfeiffer.com/golang-testing-benchmark-profiling-subtests-fuzz-testing/#running-specific-benchmarks"&gt;https://blog.john-pfeiffer.com/golang-testing-benchmark-profiling-subtests-fuzz-testing/#running-specific-benchmarks&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="insights"&gt;Insights&lt;/h2&gt;
&lt;p&gt;The hardest part of applying caching is understanding the problem well enough to see where the extra work can be avoided.
Thus I recommend pen and paper (or whiteboard) for diagramming the tree of (usually recursive) calls in order to see any patterns.&lt;/p&gt;
&lt;p&gt;The tradeoff of memory for computation (time!) is usually worth it given modern large amounts of cheap memory available (assuming we do not have to worry about cache invalidation ;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://blog.john-pfeiffer.com/caching-data-and-common-gotchas-and-an-intro-to-redis-memcached-and-varnish/"&gt;https://blog.john-pfeiffer.com/caching-data-and-common-gotchas-and-an-intro-to-redis-memcached-and-varnish/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Further exercises:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the coin problem (do not need to re-calculate the sub problems) , &lt;a href="https://en.wikipedia.org/wiki/Coin_problem"&gt;https://en.wikipedia.org/wiki/Coin_problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;towers of hanoi instructions series (rotate previous instructions rather than full recursion) , &lt;a href="https://en.wikipedia.org/wiki/Tower_of_Hanoi"&gt;https://en.wikipedia.org/wiki/Tower_of_Hanoi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="programming"/><category term="dynamic programming"/><category term="memoization"/><category term="benchmark"/><category term="golang"/><category term="play.golang.org"/><category term="time"/><category term="milliseconds"/></entry><entry><title>Productivity is a Myth</title><link href="https://blog.john-pfeiffer.com/productivity-is-a-myth/" rel="alternate"/><published>2017-06-29T21:00:00-07:00</published><updated>2017-06-29T21:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2017-06-29:/productivity-is-a-myth/</id><summary type="html">
&lt;p&gt;Productivity is dependent on and interwoven with so many things that anytime anyone pushes "more productive" I wonder...&lt;/p&gt;
&lt;p&gt;If you're "productive" and you get 3 things done in a day, or in a week even...&lt;/p&gt;
&lt;h3 id="the-many-measures-of-productivity"&gt;The Many Measures of Productivity&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Were the 3 things done actually the important ones? &lt;em&gt;(i …&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">
&lt;p&gt;Productivity is dependent on and interwoven with so many things that anytime anyone pushes "more productive" I wonder...&lt;/p&gt;
&lt;p&gt;If you're "productive" and you get 3 things done in a day, or in a week even...&lt;/p&gt;
&lt;h3 id="the-many-measures-of-productivity"&gt;The Many Measures of Productivity&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Were the 3 things done actually the important ones? &lt;em&gt;(i.e. do they create &lt;/em&gt;&lt;em&gt;value&lt;/em&gt;&lt;em&gt;? reduce drag or improve velocity?)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;How are you able to actually measure the effect/impact of the 3 things?  Against what baseline and over what time period?&lt;/li&gt;
&lt;li&gt;Do those 3 things even matter if the market suddenly shifted away from your product/segment/industry?&lt;/li&gt;
&lt;li&gt;Do they contradict previous features/changes? &lt;em&gt;(i.e. did they create confusion rather than clarity?)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Were 3 things being compared to 1, or were they being compared to 30? &lt;em&gt;(If &lt;/em&gt;&lt;em&gt;expectations&lt;/em&gt;&lt;em&gt; aren't correctly set and met you cannot ever be "productive")&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Does anybody even know that you did those 3 things? &lt;em&gt;("if a tree falls..." and "success has many fathers")&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="hidden-costs"&gt;Hidden Costs&lt;/h3&gt;
&lt;p&gt;When getting 3 things "done", did you ignore the cost of quality?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Does it actually solve the intended problem?&lt;/li&gt;
&lt;li&gt;Are there unacceptable side effects or regressions?&lt;/li&gt;
&lt;li&gt;Do you even want to know the answers to the previous two questions?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Without test automation you probably have a feature or code that cannot be validated:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How will this value persist when the next bug fix or refactor occurs?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With code that is &lt;strong&gt;unreadable&lt;/strong&gt; and therefore unmaintainable:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Will the next developer be unable to fix a bug or maybe just removes the code entirely?&lt;/li&gt;
&lt;li&gt;Will you be unable to grow your organization as you cannot find anyone to work on your codebase anymore?&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Did these changes dramatically increase the &lt;strong&gt;complexity&lt;/strong&gt; of the architecture or the product?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;spaghetti-ball-of-mud-rube-goldberg-birth-of-a-blackhole?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Was this "done" at the expense of &lt;strong&gt;burnout&lt;/strong&gt; of an individual or the team?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Who will continue development of the feature or product?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;How do these changes affect operational complexity or cost?  Does crashing often or going &lt;strong&gt;bankrupt faster&lt;/strong&gt; count as productivity?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is this just squeezing the complexity bubble or shifting the burden?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In "getting things done" did you &lt;strong&gt;burn bridges&lt;/strong&gt; within your organization? What about partners or existing customers?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Did you make a small subset happier at the expense of a much larger group?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What about the future, did you just trade loyal customers for a new ephemeral cohort?&lt;/p&gt;
&lt;h3 id="johns-phases-of-value"&gt;John's Phases of Value&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Least Valuable&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;an unshared idea
a proposal
a plan
some code
a pushed commit
in testing in a branch
reviewed and merged to master
testing the deployment (alpha)
released and distributed
experimental usage with feedback (beta)
stable usage
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Most Valuable&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Things become more valuable as they become more real&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;(becoming widely distributed/adopted for more people is a kind of proof that it actually solves a problem)&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="traditional-methods-of-productivity"&gt;Traditional Methods of Productivity&lt;/h3&gt;
&lt;p&gt;There are amazing methods and techniques to "sustainably" and reliably create the "right thing" that is the "tipping point feature" of a "killer app" where you can measure and watch your key metrics and indicators go "up and to the right".&lt;/p&gt;
&lt;p&gt;"Everyone loves a winner" and survivor bias certainly helps but...&lt;/p&gt;
&lt;p&gt;The simplest way, in our capitalism-with-money-as-a-fungible-intermediary, is to receive dollars. :$&lt;/p&gt;
&lt;p&gt;Yup, very crass, and a "lagging indicator" that is not nearly as exciting as "clicks" or "eyeballs" or "MAU/DAU/HAU", but...
&lt;em&gt;Monthly Active Users, Daily Active Users, Hourly Active Users, etc.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When you get people to part with their money you have definitely convinced them of &lt;strong&gt;some value&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Organizations that have lasted longer, with the same people in them, are more likely the ones that have figured out a process for delivering value.&lt;/p&gt;
&lt;p&gt;But maybe, instead of focusing on being productive, you should figure out how to get old and rich. =p&lt;/p&gt;
&lt;h3 id="doing-something-different"&gt;Doing Something Different&lt;/h3&gt;
&lt;p&gt;While imitation is the sincerest form of attempting to copy someone else's financial success, competition in a "race to the bottom" can turn a "win-win" into a "zero-sum" and then a "lose-lose".&lt;/p&gt;
&lt;p&gt;Productivity should therefore also be compared against not only "what-is" but the "not-yet-imagined".&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Strategy is creating &lt;strong&gt;a rising tide&lt;/strong&gt; that lifts all boats.  Tactics is poking holes in other people's stuff&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;So go on then, be productive! =)&lt;/strong&gt;&lt;/p&gt;</content><category term="puzzles"/><category term="productivity"/><category term="definition of done"/><category term="testing"/><category term="management"/></entry><entry><title>Continuous Delivery with Bitbucket Pipelines and Google App Engine Deployment and the storage.objects.list error</title><link href="https://blog.john-pfeiffer.com/continuous-delivery-with-bitbucket-pipelines-and-google-app-engine-deployment-and-the-storageobjectslist-error/" rel="alternate"/><published>2017-05-23T22:24:00-07:00</published><updated>2017-05-23T22:24:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2017-05-23:/continuous-delivery-with-bitbucket-pipelines-and-google-app-engine-deployment-and-the-storageobjectslist-error/</id><summary type="html">
&lt;p&gt;One thing that critical to writing software is actually delivering value.  That means shipping the bits, luckily the internet makes the cost of moving 1's and 0's near zero.&lt;/p&gt;
&lt;p&gt;Like any other chore or drudge work the release process should be automated.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Continuous_delivery"&gt;https://en.wikipedia.org/wiki/Continuous_delivery&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I attempted …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;One thing that critical to writing software is actually delivering value.  That means shipping the bits, luckily the internet makes the cost of moving 1's and 0's near zero.&lt;/p&gt;
&lt;p&gt;Like any other chore or drudge work the release process should be automated.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Continuous_delivery"&gt;https://en.wikipedia.org/wiki/Continuous_delivery&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I attempted to follow Google App Engine's documentation on how to setup Bitbucket Pipelines (yeah it's crazy how much free compute there is in the world such that I can tie together a free Continuous Integration service with a free Application Hosting service) but ran into a few snags I thought I'd document.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Leveraging SaaS source control, CI, and CD means inherently placing trust in those vendors (and their ops and security teams)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="google-cloud-setup"&gt;Google Cloud Setup&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://cloud.google.com/solutions/continuous-delivery-bitbucket-app-engine"&gt;https://cloud.google.com/solutions/continuous-delivery-bitbucket-app-engine&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Besides creating an App Engine Project (python) also create the credentials to allow the automated deployment.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a new Cloud project in Google: &lt;a href="https://console.cloud.google.com/projectcreate"&gt;https://console.cloud.google.com/projectcreate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Also create a new App Engine Project: &lt;a href="https://console.cloud.google.com/projectselector/appengine/create?lang=python&amp;amp;st=true"&gt;https://console.cloud.google.com/projectselector/appengine/create?lang=python&amp;amp;st=true&lt;/a&gt; (you will need to choose your region such as us-central, or use the Web UI &lt;a href="https://console.cloud.google.com/iam-admin/iam/project?project=example-john"&gt;https://console.cloud.google.com/iam-admin/iam/project?project=example-john&lt;/a&gt; (Use the "Select a project dropdown" in the middle and in the Select menu use the + symbol on the right to "Create project")&lt;/li&gt;
&lt;li&gt;Note the ID: &lt;a href="https://console.cloud.google.com/home/dashboard?project=example-john"&gt;https://console.cloud.google.com/home/dashboard?project=example-john&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;BILLING on the left, enable that (and link your credit card I guess, Google are moving everything away from free - and the credit card is another way to track and correlate everything you do)&lt;/li&gt;
&lt;li&gt;Enable the App Engine Admin API &lt;a href="https://console.cloud.google.com/apis/library?project=example-john"&gt;https://console.cloud.google.com/apis/library?project=example-john&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Navigate to the Google Cloud Platform Console credentials page &lt;a href="https://console.cloud.google.com/apis/credentials?project=example-john"&gt;https://console.cloud.google.com/apis/credentials?project=example-john&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Click Create credentials -&amp;gt; Service account key&lt;/li&gt;
&lt;li&gt;Select "New service account" from the Service account dropdown&lt;/li&gt;
&lt;li&gt;Input a name like "Bitbucket authorization" in the Service account name field&lt;/li&gt;
&lt;li&gt;ENSURE the ROLES contain at least: App Engine -&amp;gt; &lt;strong&gt;App Engine Admin&lt;/strong&gt; and Storage -&amp;gt; &lt;strong&gt;Storage Admin&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Click the Create button. A copy of the JSON file will automatically download to your computer.   (if necessary on the right three dots choose "create key" -&amp;gt; create private key for "..." JSON&lt;/li&gt;
&lt;li&gt;Click Create credentials &amp;gt; API key&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="bitbucket-setup"&gt;Bitbucket Setup&lt;/h2&gt;
&lt;p&gt;Create a new repository in bitbucket that will contain the code deployed to Google App Engine (in my example I'll use the Python native one but I know soon everything will Docker based)&lt;/p&gt;
&lt;p&gt;The minimum version of the two app engine python app you will need are:&lt;/p&gt;
&lt;h3 id="appyaml"&gt;app.yaml&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python27&lt;/span&gt;
&lt;span class="n"&gt;api_version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;threadsafe&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;yes&lt;/span&gt;

&lt;span class="n"&gt;handlers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;app&lt;/span&gt;

&lt;span class="n"&gt;libraries&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webapp2&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.5.2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For more info &lt;a href="https://developers.google.com/appengine/docs/python/config/appconfig"&gt;https://developers.google.com/appengine/docs/python/config/appconfig&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="mainpy"&gt;main.py&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;
&lt;span class="normal"&gt;8&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;webapp2&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WSGIApplication&lt;/span&gt;&lt;span class="p"&gt;([(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MainHandler&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;A previous example of writing a Python web app with Google App Engine &lt;a href="https://blog.john-pfeiffer.com/google-app-engine-python/"&gt;https://blog.john-pfeiffer.com/google-app-engine-python/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="bitbucket-pipelines-configuration"&gt;Bitbucket Pipelines Configuration&lt;/h2&gt;
&lt;p&gt;Enable the Pipelines feature: &lt;a href="https://bitbucket.org/johnpfeiffer/continuous-deployment-bitbucket/addon/pipelines/home#!/getting-started"&gt;https://bitbucket.org/johnpfeiffer/continuous-deployment-bitbucket/addon/pipelines/home#!/getting-started&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="configuring-the-secure-environment-variables-in-bitbucket-pipelines"&gt;Configuring the secure environment variables in Bitbucket Pipelines&lt;/h3&gt;
&lt;p&gt;Use the Bitbucket WebUI in order to securely add the three variables (project id, api key, secrets json).&lt;/p&gt;
&lt;p&gt;i.e. &lt;a href="https://bitbucket.org/johnpfeiffer/continuous-deployment-bitbucket/admin/addon/admin/pipelines/repository-variables"&gt;https://bitbucket.org/johnpfeiffer/continuous-deployment-bitbucket/admin/addon/admin/pipelines/repository-variables&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CLOUDSDK_CORE_PROJECT (the app engine project id)&lt;/li&gt;
&lt;li&gt;GOOGLE_API_KEY&lt;/li&gt;
&lt;li&gt;GOOGLE_CLIENT_SECRET (all of the contents of the json file pasted in)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Ensure the Pipelines Environment variable of GOOGLE_CLIENT_SECRET is "Secured"&lt;/strong&gt; so it is encrypted (i.e. all asterisks in the forms or in the log output)&lt;/p&gt;
&lt;p&gt;Full instructions: &lt;a href="https://cloud.google.com/solutions/continuous-delivery-bitbucket-app-engine#setting_up_environment_variables"&gt;https://cloud.google.com/solutions/continuous-delivery-bitbucket-app-engine#setting_up_environment_variables&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="bitbucket-pipelinesyml-file"&gt;bitbucket-pipelines.yml file&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Before using a secret (or really any environment variables) with bitbucket pipelines you will need to have a placeholder bitbucket-pipelines.yml so that you can officially "enable" it for the repository - after that the WebUI for configuration for pipelines will become accessible =(&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;2.7&lt;/span&gt;
&lt;span class="n"&gt;pipelines&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Use this very trivial bitbucket-pipelines.yml in order to get at least one successful validation so that you can press the "Enable" button in the Bitbucket UI (I had to zoom my browser out to workaround some broken css/javascript)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A yaml configuration file describes the work that you instruct Bitbucket Pipelines to do, in this case we are doing the extra work of grabbing the remote google cloud SDK and installing it so that we use it with the credentials to deploy the app.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;2.7&lt;/span&gt;

&lt;span class="n"&gt;pipelines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CLOUDSDK_CORE_DISABLE_PROMPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SDK_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;127.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SDK_FILENAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cloud&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sdk&lt;/span&gt;&lt;span class="o"&gt;-$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SDK_VERSION&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;linux&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x86_64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;O&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;J&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;dl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dl&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cloudsdk&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rapid&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;downloads&lt;/span&gt;&lt;span class="o"&gt;/$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SDK_FILENAME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zxvf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SDK_FILENAME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;HOME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;HOME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cloud&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sdk&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GAE_PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;HOME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;google_appengine&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;GAE_PYTHONPATH&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;fetch_gae_sdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${GAE_PYTHONPATH}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${PYTHONPATH}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;GAE_PYTHONPATH&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"key = '${GOOGLE_API_KEY}'"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;GOOGLE_CLIENT_SECRET&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gcloud&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;activate&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gcloud&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;verbosity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deploy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;promote&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;A best practice is to pull the dependency from storage you have control over (or simply vendor the SDK in the source code) rather than downloading it every time and risking the upstream pinned version being removed&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bitbucket-pipelines.prod.public.atl-paas.net/validator"&gt;https://bitbucket-pipelines.prod.public.atl-paas.net/validator&lt;/a&gt; did not work for me (crazy javascript) but in theory this would be a very helpful tool&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Full bitbucket pipelines documentation: &lt;a href="https://confluence.atlassian.com/bitbucket/build-test-and-deploy-with-pipelines-792496469.html"&gt;https://confluence.atlassian.com/bitbucket/build-test-and-deploy-with-pipelines-792496469.html&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="scriptsfetch_gae_sdkpy"&gt;scripts/fetch_gae_sdk.py&lt;/h3&gt;
&lt;hr/&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c1"&gt;# Copyright 2015 Google Inc. All rights reserved.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Licensed under the Apache License, Version 2.0 (the "License");&lt;/span&gt;
&lt;span class="c1"&gt;# you may not use this file except in compliance with the License.&lt;/span&gt;
&lt;span class="c1"&gt;# You may obtain a copy of the License at&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;#     http://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Unless required by applicable law or agreed to in writing, software&lt;/span&gt;
&lt;span class="c1"&gt;# distributed under the License is distributed on an "AS IS" BASIS,&lt;/span&gt;
&lt;span class="c1"&gt;# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;
&lt;span class="c1"&gt;# See the License for the specific language governing permissions and&lt;/span&gt;
&lt;span class="c1"&gt;# limitations under the License.&lt;/span&gt;

&lt;span class="c1"&gt;# Retrieved from https://github.com/Google/oauth2client&lt;/span&gt;
&lt;span class="sd"&gt;"""Fetch the most recent GAE SDK and decompress it in the current directory.&lt;/span&gt;
&lt;span class="sd"&gt;Usage:&lt;/span&gt;
&lt;span class="sd"&gt;    fetch_gae_sdk.py [&amp;lt;dest_dir&amp;gt;]&lt;/span&gt;
&lt;span class="sd"&gt;Current releases are listed here:&lt;/span&gt;
&lt;span class="sd"&gt;    https://www.googleapis.com/storage/v1/b/appengine-sdks/o?prefix=featured&lt;/span&gt;
&lt;span class="sd"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;StringIO&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;urllib2&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;zipfile&lt;/span&gt;

&lt;span class="n"&gt;_SDK_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'https://www.googleapis.com/storage/v1/b/appengine-sdks/o?prefix=featured'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_gae_versions&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;version_info_json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_SDK_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;version_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version_info_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'items'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_version_tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;version_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;splitext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpartition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'_'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;version_string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_sdk_urls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sdk_versions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;python_releases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sdk_versions&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'featured/google_appengine'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;current_releases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;python_releases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_version_tuple&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'mediaLink'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;release&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;current_releases&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Usage: &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt; [&amp;lt;destination_dir&amp;gt;]'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;dest_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s1"&gt;'.'&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;makedirs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'google_appengine'&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GAE SDK already installed at &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;, exiting.'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_dir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="n"&gt;sdk_versions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_gae_versions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;sdk_versions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Error fetching GAE SDK version info'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;sdk_urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_sdk_urls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sdk_versions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sdk_url&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sdk_urls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;sdk_contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sdk_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Could not read SDK from any of &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sdk_urls&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;sdk_contents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;zip_contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zipfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZipFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sdk_contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;zip_contents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extractall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GAE SDK Installed to &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;.'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_dir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Error extracting SDK contents'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'__main__'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[:]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This is google's script to download and extract their App Engine SDK , included here only for completeness&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="push-a-commit-to-trigger-a-deployment"&gt;Push a commit to trigger a deployment&lt;/h2&gt;
&lt;p&gt;The whole point of all of that work is to make life easier for every commit that follows.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git add --all .
git commit -m 'example google app engine app deployed via bitbucket pipelines'
git push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can monitor the results of every change in the Addon's output logs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bitbucket.org/johnpfeiffer/continuous-deployment-bitbucket/addon/pipelines/home#!/"&gt;https://bitbucket.org/johnpfeiffer/continuous-deployment-bitbucket/addon/pipelines/home#!/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can monitor the deployment history in Google App Engine's console:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://console.cloud.google.com/appengine/versions?project=bitbucket-pipelines&amp;amp;serviceId=default"&gt;https://console.cloud.google.com/appengine/versions?project=bitbucket-pipelines&amp;amp;serviceId=default&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And of course see the currently deployed application in action:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bitbucket-pipelines.appspot.com/"&gt;https://bitbucket-pipelines.appspot.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="actually-using-automated-testing"&gt;Actually using automated testing&lt;/h2&gt;
&lt;p&gt;CI (continuous integration) testing is the ideal next thing to add as this allows you to automatically prevent critical errors from being deployed to production.&lt;/p&gt;
&lt;p&gt;This means adding unit tests (test_main.py) and then running them by updating bitbucket-pipelines.yml&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;-&lt;/span&gt; python test_main.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It is also possible to keep extending the work required by installing any requirements.txt dependencies (i.e. only required for testing) or running post deployment end-to-end smoke tests.&lt;/p&gt;
&lt;h2 id="double-checking-security"&gt;Double checking security&lt;/h2&gt;
&lt;p&gt;If your repository is public the pipelines log outputs will be public.  Double check that you are not "leaking" your API key, secrets, or hardcoded passwords. =|&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The current Google Cloud project [go-lanscan] does not contain an App Engine application. Use &lt;code&gt;gcloud app create&lt;/code&gt; to initialize an App Engine application within the project.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You need to create the initial app engine project, either with the WebUI &lt;a href="https://console.cloud.google.com/appengine/start?project=bitbucket-pipelines"&gt;https://console.cloud.google.com/appengine/start?project=bitbucket-pipelines&lt;/a&gt; or using the GCloud CLI&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;You will need to be prompted for your region, i.e. us-central&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;"Does not contain a valid app engine project"&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Most likely the app.yaml has deprecated fields or invalid characters&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;"does not contain an App Engine application."&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Most likely the app.yaml has deprecated fields or invalid characters, or maybe missing a main.py altogether&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;"ERROR: The [application] field is specified in file [/opt/atlassian/pipelines/agent/build/app.yaml]"&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is because previously app.yaml included "application" and "version" but those lines are now deprecated, delete them&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;"You do not have permission to access app "&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Most likely the Role of "App Engine Admin" still needs to be added, use the IAM for the project to update the Permissions&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;e.g. &lt;a href="https://console.cloud.google.com/iam-admin/iam/project?project=bitbucket-pipelines"&gt;https://console.cloud.google.com/iam-admin/iam/project?project=bitbucket-pipelines&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Or ensure you have an API key generated and added to the Bitbucket Pipelines environment ... or delete all API keys and service accounts and do them again (because it seems to get stuck if you have encrypted API key or something)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://console.cloud.google.com/apis/credentials?project=bitbucket-pipelines"&gt;https://console.cloud.google.com/apis/credentials?project=bitbucket-pipelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://console.cloud.google.com/iam-admin/serviceaccounts/project?project=bitbucket-pipelines"&gt;https://console.cloud.google.com/iam-admin/serviceaccounts/project?project=bitbucket-pipelines&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;"Caller does not have storage.objects.list access to bucket staging.bitbucket-pipelines.appspot.com."&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ensure the Role "Storage Object Admin" was added to the Roles during creation, see above&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This guy got really close and helped me find the hint about Storage Object Admin: &lt;a href="http://www.deadunicornz.org/blog/2017/01/31/travis-ci-and-deploying-golang-apps-to-gae/"&gt;http://www.deadunicornz.org/blog/2017/01/31/travis-ci-and-deploying-golang-apps-to-gae/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="using-docker-with-bitbucket-pipelines"&gt;Using Docker with Bitbucket Pipelines&lt;/h2&gt;
&lt;p&gt;I could reduce the time required to build (and decouple the build stages) by using a Docker image that already contains the GCloud SDK.&lt;/p&gt;
&lt;p&gt;This also extends the flexibility if part of your build flow is to build and tag a Docker image as an artifact that can be used for multiple tests (i.e. parallelization) and especially if you are already using Docker in Production.&lt;/p&gt;
&lt;h3 id="create-an-automated-docker-image-build-in-docker-hub"&gt;Create an automated docker image build in Docker Hub&lt;/h3&gt;
&lt;p&gt;Link a source code repository to Docker (sadly the bitbucket one requires overly broad permissions whereas the github one only requires public read)&lt;/p&gt;
&lt;p&gt;Follow the steps in their tutorial to create a repository with a Dockerfile and then have it auto-build an image and upload it to Docker Hub&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/docker-hub/builds/"&gt;https://docs.docker.com/docker-hub/builds/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/r/foupfeiffer/gcloud-sdk/~/dockerfile/"&gt;https://hub.docker.com/r/foupfeiffer/gcloud-sdk/~/dockerfile/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Unfortunately sometimes you have to just log in to Docker Hub and press the "Trigger" button to start the build since "automatically" does not seem to trigger correctly, e.g. &lt;a href="https://hub.docker.com/r/foupfeiffer/gcloud-sdk/~/settings/automated-builds/"&gt;https://hub.docker.com/r/foupfeiffer/gcloud-sdk/~/settings/automated-builds/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="using-a-public-image-from-docker-hub"&gt;Using a public image from Docker Hub&lt;/h3&gt;
&lt;p&gt;To verify App Engine publishing from the Docker container manually: 
&lt;code&gt;docker run --rm -it --volume /opt/mysecrets:/opt/mysecrets --volume /opt/myrepo:/opt/myrepo foupfeiffer/gcloud-sdk /bin/bash&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Run these commands in the Docker container...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mysecrets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myrepo&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CLOUDSDK_CORE_DISABLE_PROMPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CLOUDSDK_CORE_PROJECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;gae&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myrepo&lt;/span&gt;
&lt;span class="n"&gt;gcloud&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;activate&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mysecrets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="n"&gt;gcloud&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;verbosity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deploy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;promote&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;You are about to deploy the following services: Uploading 7 files to Google Cloud Storage&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;This assumes you have already correctly setup your Google Cloud Project, Google App Engine Project, Google Permissions, and that your repository has the correct app.yaml and main.py&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="updating-your-bitbucket-pipelinesyml-to-use-a-public-docker-image"&gt;Updating your bitbucket-pipelines.yml to use a public docker image&lt;/h3&gt;
&lt;p&gt;With your open sourced Dockerfile and docker image built in docker hub we can simplify some of the steps in our previous bitbucket-pipelines.yml file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;foupfeiffer&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gcloud&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sdk&lt;/span&gt;

&lt;span class="n"&gt;pipelines&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CLOUDSDK_CORE_DISABLE_PROMPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"key = '${GOOGLE_API_KEY}'"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;py&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="n"&gt;GOOGLE_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;json&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gcloud&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;activate&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;json&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gcloud&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;verbosity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deploy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;yaml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;promote&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;One you have pushed this change every future build will have the capabilities of your docker image (i.e. go build)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confluence.atlassian.com/bitbucket/use-docker-images-as-build-environments-in-bitbucket-pipelines-792298897.html"&gt;https://confluence.atlassian.com/bitbucket/use-docker-images-as-build-environments-in-bitbucket-pipelines-792298897.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confluence.atlassian.com/bitbucket/debug-your-pipelines-locally-with-docker-838273569.html"&gt;https://confluence.atlassian.com/bitbucket/debug-your-pipelines-locally-with-docker-838273569.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="bitbucket-pipelines-and-go"&gt;Bitbucket Pipelines and Go&lt;/h2&gt;
&lt;p&gt;This article was about deploying a python application (which in my use case happened to distribute a Go binary).&lt;/p&gt;
&lt;p&gt;For more information about a Golang application with Go Build and Tests...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bitbucket.org/atlassian/pipelines-examples-go/"&gt;https://bitbucket.org/atlassian/pipelines-examples-go/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bitbucket.org/atlassian/pipelines-examples-go/src/master/bitbucket-pipelines.yml"&gt;https://bitbucket.org/atlassian/pipelines-examples-go/src/master/bitbucket-pipelines.yml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bitbucket-pipelines.prod.public.atl-paas.net/validator"&gt;https://bitbucket-pipelines.prod.public.atl-paas.net/validator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="bitbucket-pipelines-and-testing-with-a-database"&gt;Bitbucket Pipelines and Testing with a Database&lt;/h2&gt;
&lt;p&gt;There is an example of how to leverage two of the most common databases, MySQL and Postgres, in your testing pipeline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confluence.atlassian.com/bitbucket/test-with-databases-in-bitbucket-pipelines-856697462.html"&gt;https://confluence.atlassian.com/bitbucket/test-with-databases-in-bitbucket-pipelines-856697462.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Obviously since you can execute the pipeline with your own customer Docker image you can extend and build your required test/environment dependencies.&lt;/p&gt;</content><category term="build-CI-CD-devops"/><category term="bitbucket"/><category term="google app engine"/><category term="gae"/><category term="pipelines"/><category term="ci"/><category term="cd"/><category term="deployment"/><category term="storage.objects.list"/></entry><entry><title>Mid 2017 technology and business prediction for 2018 and beyond</title><link href="https://blog.john-pfeiffer.com/mid-2017-technology-and-business-prediction-for-2018-and-beyond/" rel="alternate"/><published>2017-05-07T20:00:00-07:00</published><updated>2017-05-07T20:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2017-05-07:/mid-2017-technology-and-business-prediction-for-2018-and-beyond/</id><summary type="html">
&lt;h1 id="the-future-already-happened"&gt;The future already happened&lt;/h1&gt;
&lt;p&gt;The internet boom has arrived silently and the "everything is online" happened while we were all looking down at our smart phones.&lt;/p&gt;
&lt;p&gt;New giants have emerged to rule the economic landscape that have eclipsed, at least in virtual specie, their physical-and-brick-and-mortar competitors: Apple, Alphabet (Google), Microsoft …&lt;/p&gt;</summary><content type="html">
&lt;h1 id="the-future-already-happened"&gt;The future already happened&lt;/h1&gt;
&lt;p&gt;The internet boom has arrived silently and the "everything is online" happened while we were all looking down at our smart phones.&lt;/p&gt;
&lt;p&gt;New giants have emerged to rule the economic landscape that have eclipsed, at least in virtual specie, their physical-and-brick-and-mortar competitors: Apple, Alphabet (Google), Microsoft, Amazon.com... Facebook...&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/List_of_public_corporations_by_market_capitalization#2017"&gt;https://en.wikipedia.org/wiki/List_of_public_corporations_by_market_capitalization#2017&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="the-market-can-stay-irrational-longer-than-you-can-stay-solvent"&gt;The market can stay irrational longer than you can stay solvent&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;-John Maynard Keynes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We have entered into an uncharted area in terms of economics; much financial data and many models indicate that 2017 is very near the peak of a trend.&lt;/p&gt;
&lt;p&gt;The Case-Shiller Home Price Index is topping the maximums of 2006 and almost double the "normal" of 100 and the CyclicallyAdjustedPriceEarnings ratio is at 29, also soon to be double the median of 16.  Neither of these tools are intended for "timing the market" or identifying the end of a trend, but they certainly indicate a longer wait for expected return on investment and higher level of risk of a "return to the mean".&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The CAPE ratio hit 44 right before the "dot-com" bust (pokerface)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fred.stlouisfed.org/series/CSUSHPINSA"&gt;https://fred.stlouisfed.org/series/CSUSHPINSA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://us.spindices.com/index-family/real-estate/sp-corelogic-case-shiller"&gt;https://us.spindices.com/index-family/real-estate/sp-corelogic-case-shiller&lt;/a&gt; 188 is near an all time high (of 195 in 2006)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Case%E2%80%93Shiller_index#History_and_methodology"&gt;https://en.wikipedia.org/wiki/Case%E2%80%93Shiller_index#History_and_methodology&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ycharts.com/indicators/pe10"&gt;https://ycharts.com/indicators/pe10&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Cyclically_adjusted_price-to-earnings_ratio"&gt;https://en.wikipedia.org/wiki/Cyclically_adjusted_price-to-earnings_ratio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.businessinsider.com/robert-shiller-explains-how-to-use-cape-2013-11"&gt;http://www.businessinsider.com/robert-shiller-explains-how-to-use-cape-2013-11&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Dot-com_bubble#Stock_market_bubble"&gt;https://en.wikipedia.org/wiki/Dot-com_bubble#Stock_market_bubble&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most optimistic perspective is that globalization and technology have dramatically increasing efficiencies, though that same combination drove the "Roaring 20's" and (along with political and economic factors) created the "Great Depression".&lt;/p&gt;
&lt;h1 id="prediction-of-a-crash"&gt;Prediction of a Crash&lt;/h1&gt;
&lt;p&gt;I'll make a prediction (not that it will change the future in any way).  Given the cyclical nature of economies, it has to come to pass sometime ;]&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The United States is due for an economic crash soon&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At a minimum a recession, most likely another "Great Recession", hopefully not another "Great Depression".&lt;/p&gt;
&lt;p&gt;There will most likely be a catalyst like unexpected political collapse (Presidential impeachment), armed conflict (i.e. North Korea), or environmental disaster (i.e. global pandemic or solar flare).&lt;/p&gt;
&lt;p&gt;Technology is not a root cause but creates the sense of instability in society and economics as efficiencies disrupt existing balances.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A "perfect storm" of global economic weakness with globalization that interconnects all economies&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://web-beta.archive.org/web/20170409211343/https://www.visualcapitalist.com/chart-epic-collapse-deutsche-bank/"&gt;https://web-beta.archive.org/web/20170409211343/https://www.visualcapitalist.com/chart-epic-collapse-deutsche-bank/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.telegraph.co.uk/business/2016/07/16/why-italys-banking-crisis-will-shake-the-eurozone-to-its-core/"&gt;http://www.telegraph.co.uk/business/2016/07/16/why-italys-banking-crisis-will-shake-the-eurozone-to-its-core/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fortune.com/2016/01/23/china-collapse/"&gt;https://fortune.com/2016/01/23/china-collapse/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Populist moves to nationalism and tariffs&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.theguardian.com/business/2016/aug/17/brexit-trade-deals-gruelling-challenge-taking-back-control"&gt;https://www.theguardian.com/business/2016/aug/17/brexit-trade-deals-gruelling-challenge-taking-back-control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bloomberg.com/politics/articles/2017-01-18/why-trump-s-tariff-threats-get-taken-so-seriously-quicktake-q-a"&gt;https://www.bloomberg.com/politics/articles/2017-01-18/why-trump-s-tariff-threats-get-taken-so-seriously-quicktake-q-a&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Smoot%E2%80%93Hawley_Tariff_Act"&gt;https://en.wikipedia.org/wiki/Smoot%E2%80%93Hawley_Tariff_Act&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Deregulation and "pro-business" policies&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nytimes.com/2017/02/03/business/dealbook/trump-congress-financial-regulations.html"&gt;https://www.nytimes.com/2017/02/03/business/dealbook/trump-congress-financial-regulations.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Warren_G._Harding#Scandals"&gt;https://en.wikipedia.org/wiki/Warren_G._Harding#Scandals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Calvin_Coolidge#Taxation_and_government_spending"&gt;https://en.wikipedia.org/wiki/Calvin_Coolidge#Taxation_and_government_spending&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Herbert_Hoover#Presidential_election_of_1928"&gt;https://en.wikipedia.org/wiki/Herbert_Hoover#Presidential_election_of_1928&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Minsky_moment"&gt;https://en.wikipedia.org/wiki/Minsky_moment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Great_Depression"&gt;https://en.wikipedia.org/wiki/Great_Depression&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="increased-migration"&gt;Increased Migration&lt;/h2&gt;
&lt;p&gt;In the wake of global instability people will seek, in ever larger numbers, societies and infrastructure that are stable.&lt;/p&gt;
&lt;p&gt;Those places that adopt policies and mechanisms to successfully integrate and take advantage of the influx of newcomers will do quite well.&lt;/p&gt;
&lt;p&gt;Those places that attempt to remain isolated will incur ever growing costs and simultaneously be outcompeted by places that have successfully leveraged new ideas, new labor, and new capital.&lt;/p&gt;
&lt;h1 id="technology-predictions"&gt;Technology Predictions&lt;/h1&gt;
&lt;p&gt;We are rapidly moving into a new era of technology and information where energy is becoming cheaper than ever and computing has become more powerful and ubiquitous than ever.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.bloomberg.com/news/articles/2016-12-15/world-energy-hits-a-turning-point-solar-that-s-cheaper-than-wind"&gt;https://www.bloomberg.com/news/articles/2016-12-15/world-energy-hits-a-turning-point-solar-that-s-cheaper-than-wind&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/List_of_countries_by_number_of_mobile_phones_in_use"&gt;https://en.wikipedia.org/wiki/List_of_countries_by_number_of_mobile_phones_in_use&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.pewinternet.org/2015/04/01/us-smartphone-use-in-2015/"&gt;http://www.pewinternet.org/2015/04/01/us-smartphone-use-in-2015/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.businessinsider.com/24-mind-blowing-facts-about-business-2013-6?op=1"&gt;http://www.businessinsider.com/24-mind-blowing-facts-about-business-2013-6?op=1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="scarcity-of-privacy"&gt;Scarcity of Privacy&lt;/h2&gt;
&lt;p&gt;The proliferation of smart phones with cameras everywhere, drones, actively listening devices like Siri/Alexa/Google means that it is far harder than ever to be alone, unwatched, and unheard.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.businessinsider.com/siri-vs-google-assistant-cortana-alexa-2016-11/"&gt;http://www.businessinsider.com/siri-vs-google-assistant-cortana-alexa-2016-11/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.economist.com/news/science-and-technology/21666118-miniature-pilotless-aircraft-are-verge-becoming-commonplace-welcome"&gt;http://www.economist.com/news/science-and-technology/21666118-miniature-pilotless-aircraft-are-verge-becoming-commonplace-welcome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://money.cnn.com/2017/03/28/technology/us-drone-registrations/index.html"&gt;http://money.cnn.com/2017/03/28/technology/us-drone-registrations/index.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Big data means an unprecedented amoutn of correlation can be created.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.nytimes.com/2012/02/19/magazine/shopping-habits.html"&gt;http://www.nytimes.com/2012/02/19/magazine/shopping-habits.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.wired.com/2012/08/apple-amazon-mat-honan-hacking/"&gt;https://www.wired.com/2012/08/apple-amazon-mat-honan-hacking/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.theguardian.com/technology/2014/apr/05/google-flu-big-data-help-make-gigantic-mistakes"&gt;https://www.theguardian.com/technology/2014/apr/05/google-flu-big-data-help-make-gigantic-mistakes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While this benefits society in emergency situations (i.e. earthquakes, fire, crime, etc.) it also means there will be a lucrative business opportunity in technology that can actually &lt;strong&gt;restore privacy&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://timelines.latimes.com/snapchat-stanford-ipo/"&gt;http://timelines.latimes.com/snapchat-stanford-ipo/&lt;/a&gt; (&lt;em&gt;a $30B+ social network that makes data ephemeral&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Faraday_cage"&gt;https://en.wikipedia.org/wiki/Faraday_cage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="pendulum-swings-back-to-decentralization"&gt;Pendulum swings back to decentralization&lt;/h2&gt;
&lt;p&gt;"The Cloud" is very popular now, the availability of energy and local compute (including smartphones) means it will become cheaper and cheaper to compute things locally.&lt;/p&gt;
&lt;p&gt;Even as the internet has allowed incredible flows of data and exchange of information the amount of locally generated information ("internet of things") is growing very large very quickly as well.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://techcrunch.com/2017/02/02/aws-still-owns-the-cloud/"&gt;https://techcrunch.com/2017/02/02/aws-still-owns-the-cloud/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.cnbc.com/2017/04/27/microsoft-azure-growing-faster-than-aws-google-cloud-behind.html"&gt;http://www.cnbc.com/2017/04/27/microsoft-azure-growing-faster-than-aws-google-cloud-behind.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Moore%27s_law"&gt;https://en.wikipedia.org/wiki/Moore%27s_law&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://royal.pingdom.com/2010/10/22/incredible-growth-of-the-internet-since-2000/"&gt;http://royal.pingdom.com/2010/10/22/incredible-growth-of-the-internet-since-2000/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ncta.com/sites/prod/files/GROWTH_IOT_091616_IF-800w.png"&gt;https://www.ncta.com/sites/prod/files/GROWTH_IOT_091616_IF-800w.png&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Yet given the increasing requirement for security (and privacy) it makes a lot more sense to keep data and compute inside a protected space.  One of the simplest ways to prevent hacking, keep data secure, and increase privacy is to never have the data leave its place of origin.&lt;/p&gt;
&lt;p&gt;Homes, and more importantly businesses, will not want every camera, microphone, and sensor sending data into "The Cloud" to centralized (and often vulnerable) warehouses.  Instead there is an opportunity for businesses and technology that leverage cheap compute to create &lt;strong&gt;"mini private clouds"&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The slow deprecation of SMTP and HTTP (both unencrypted plaintext) being the backbone of the internet will be increasingly replaced with secure alternatives (encrypted, dedicated networks, extra authentication layers like 2FA and MFA) to create &lt;strong&gt;virtual tunnels&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The companies and tools that allow redundancy and accessibility of data in a secure fashion will profit from the abundance of local compute/storage and scarcity of security and privacy.&lt;/p&gt;</content><category term="it"/><category term="prediction"/><category term="business model"/><category term="minsky cycle"/><category term="crash"/><category term="privacy"/><category term="decentralization"/></entry><entry><title>Golang Concurrency Goroutines and Channels</title><link href="https://blog.john-pfeiffer.com/golang-concurrency-goroutines-and-channels/" rel="alternate"/><published>2017-04-12T22:54:00-07:00</published><updated>2017-04-12T22:54:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2017-04-12:/golang-concurrency-goroutines-and-channels/</id><summary type="html">
&lt;p&gt;If there is a killer feature to Go it is the focus on concurrency.  This article captures some of the basics and I hope to someday write a follow-up article on more advanced topics.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"Go is a compiled, concurrent, garbage-collected, statically typed language"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://talks.golang.org/2012/splash.article"&gt;https://talks.golang.org/2012/splash.article …&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">
&lt;p&gt;If there is a killer feature to Go it is the focus on concurrency.  This article captures some of the basics and I hope to someday write a follow-up article on more advanced topics.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"Go is a compiled, concurrent, garbage-collected, statically typed language"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://talks.golang.org/2012/splash.article"&gt;https://talks.golang.org/2012/splash.article&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Distributed systems and large data sizes mean developers are forced to think in parallelism, or are they?&lt;/p&gt;
&lt;p&gt;Using Go channels (based upon Communicating Sequential Processes &lt;a href="https://en.wikipedia.org/wiki/Communicating_sequential_processes"&gt;https://en.wikipedia.org/wiki/Communicating_sequential_processes&lt;/a&gt;, &lt;a href="http://www.cs.cmu.edu/~crary/819-f09/Hoare78.pdf"&gt;http://www.cs.cmu.edu/~crary/819-f09/Hoare78.pdf&lt;/a&gt;) developers can write code that feels very imperative and sequential, but designed in such a way that parallelism comes easily.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org/doc/effective_go.html#concurrency"&gt;https://golang.org/doc/effective_go.html#concurrency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/concurrency-is-not-parallelism"&gt;https://blog.golang.org/concurrency-is-not-parallelism&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="go-and-goroutines"&gt;Go and Goroutines&lt;/h2&gt;
&lt;p&gt;A goroutine is a "lightweight thread" that allows for a far higher amount of concurrency than just depending on OS processes or even traditional threading (and much simpler than attempting to explicitly organize around a defined number of processors or threads).&lt;/p&gt;
&lt;p&gt;In a very meta sense every Go program uses concurrency because the main function itself is an implicit goroutine (and will not wait or might block forever ;)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"example"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This will exit without printing "example" because using the "go" keyword runs the example function in a new goroutine&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/h6_B0whHxE"&gt;https://play.golang.org/p/h6_B0whHxE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/testing/#hdr-Main"&gt;https://golang.org/pkg/testing/#hdr-Main&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/"&gt;http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Green_threads"&gt;https://en.wikipedia.org/wiki/Green_threads&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;There is no explicit external management of a goroutine once it has started, terminating a goroutine is implemented via an exception or exit in the code that the goroutine is running, usually signalled via a channel&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Since Goroutines are cooperative they are not pre-empt-able...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dave.cheney.net/2016/12/22/never-start-a-goroutine-without-knowing-how-it-will-stop"&gt;https://dave.cheney.net/2016/12/22/never-start-a-goroutine-without-knowing-how-it-will-stop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Preemption_(computing)"&gt;https://en.wikipedia.org/wiki/Preemption_(computing)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/golang/go/issues/10958"&gt;https://github.com/golang/go/issues/10958&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="sync-with-a-waitgroup"&gt;Sync with a WaitGroup&lt;/h2&gt;
&lt;p&gt;The most straightforward way to fix the previous trivial example is to specify in advance that the implicit main goroutine should wait before continuing...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"sync"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"example"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WaitGroup&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;The example function remains unchanged&lt;/li&gt;
&lt;li&gt;The waitgroup will expect one call of "Done"&lt;/li&gt;
&lt;li&gt;The go keyword now calls an anonymous function that calls wg.Done() (accessed using closure) after example()&lt;/li&gt;
&lt;li&gt;The waitgroup.Wait() blocks until the correct number of Done() calls have been made&lt;/li&gt;
&lt;li&gt;The example function sleep and print finally finish&lt;/li&gt;
&lt;li&gt;The waitgroup unblocks and the main goroutine can finally print "done" and exit&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/p0jDoiGBT4"&gt;https://play.golang.org/p/p0jDoiGBT4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/sync/#example_WaitGroup"&gt;https://golang.org/pkg/sync/#example_WaitGroup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="channels"&gt;Channels&lt;/h2&gt;
&lt;p&gt;Channels are the recommended way of communicating when using goroutines (and sharing resources).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;- the arrow always points to the left&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sleeping..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// IF NOT CLOSED THEN DEADLOCK&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// c &amp;lt;- 2  DO NOT SEND TO A CLOSED CHANNEL, IT WILL PANIC&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"anonymous function, a channel passed a value via closure"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;since a channel is a reference type "make" is used and the &amp;lt;- sends a value to the channel, later &amp;lt;- is used to receive a value&lt;/li&gt;
&lt;li&gt;a closed channel cannot be written to further and will PANIC "send on a closed channel"&lt;/li&gt;
&lt;li&gt;if you forget to close a channel, later reading from an open unbuffered channel which does not have data will exit "fatal error: all goroutines are asleep - deadlock!"&lt;/li&gt;
&lt;li&gt;an "unbuffered" channel "blocks" until both the sender and receiver are ready&lt;/li&gt;
&lt;li&gt;the Println function reads from the channel, in this case the channel acts as a synchronization tool that blocks at the Print statement and prevents the main goroutine from exiting&lt;/li&gt;
&lt;li&gt;The final printed output will be: "0 false", since the channel is closed and empty subsequent receives will return a the empty value "zero" and the state of the channel (in this case "false")&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/RR0PWmAeKa"&gt;https://play.golang.org/p/RR0PWmAeKa&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gobyexample.com/channels"&gt;https://gobyexample.com/channels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dave.cheney.net/2013/04/30/curious-channels"&gt;https://dave.cheney.net/2013/04/30/curious-channels&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="buffered-channels-and-returning-a-value"&gt;Buffered channels and returning a value&lt;/h3&gt;
&lt;p&gt;A common problem is one part of the application running faster than another part and one way to "unblock" the fast part is to use a buffer to create a queue for the slower part to catch up.
This kind of issue occurs in a "pipeline" of producers/consumers (also known as sources/sinks)...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"log"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"sync"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;slowReceiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WaitGroup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"received"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// c &amp;lt;- 42 // THIS WOULD CAUSE AN ERROR "(send to receive-only type &amp;lt;-chan int)"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fastSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WaitGroup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// fmt.Println(&amp;lt;-c) // THIS WOULD CAUSE AN ERROR "(receive from send-only type chan&amp;lt;- int)"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WaitGroup&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;slowReceiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fastSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;This example shows how to specify a channel of type that either only sends or only receives&lt;/li&gt;
&lt;li&gt;log works the same as fmt and Since() a very convenient way to output elapsed time&lt;/li&gt;
&lt;li&gt;Without the waitgroup Wait() main would exit after 1 second with only "received 1" and never reach "received 4" (5 seconds)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/pipelines"&gt;https://blog.golang.org/pipelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/NcEOgCiSQs"&gt;https://play.golang.org/p/NcEOgCiSQs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tour.golang.org/concurrency/3"&gt;https://tour.golang.org/concurrency/3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="using-select-to-not-block-a-channel"&gt;Using Select to not block a channel&lt;/h3&gt;
&lt;p&gt;Channels are most useful when they can block asynchronously until an event occurs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"time"&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fmt"&lt;/span&gt;

&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myDelayedQuit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mySleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"begin non blocking wait..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"received:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mySleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"woke up"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myDelayedQuit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;for&lt;/strong&gt; loops forever until the return statement&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;select&lt;/strong&gt; will wait and whenever a case can be filled it will unblock&lt;/li&gt;
&lt;li&gt;after 1 second the sleep function is done and sends the "woke up" message&lt;/li&gt;
&lt;li&gt;after 2 seconds the true boolean is sent and main finishes&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/JBrhHZVq6a"&gt;https://play.golang.org/p/JBrhHZVq6a&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="real-example-of-concurrency-in-a-lan-scanner"&gt;Real Example of Concurrency in a LAN Scanner&lt;/h3&gt;
&lt;p&gt;A real world example is discovering all of the hosts listening on a given port in local area network (subnet).&lt;/p&gt;
&lt;p&gt;In a serial example waiting 2 seconds for each host to respond would mean waiting 512 seconds in a "normal" /24 subnet of ~256 hosts (ignoring the .255 broadcast and .0)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IPCheckResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WaitGroup&lt;/span&gt;
&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addresses&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// https://golang.org/doc/faq#closures_and_goroutines&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;checkIP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Here the channel is simply used as a "lock free" place to aggregate all of the results of the goroutines, sync occurs via the waitgroup which will wait until every pre-added item is decremented by a Done(), there is definitely a possibility for an off-by-one gotcha that will hang your program!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://bitbucket.org/johnpfeiffer/go-lanscan/src"&gt;https://bitbucket.org/johnpfeiffer/go-lanscan/src&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="troubleshooting-race-conditions"&gt;Troubleshooting Race Conditions&lt;/h3&gt;
&lt;p&gt;A common gotcha is that in Go maps are not safe for concurrent use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/go-maps-in-action"&gt;https://blog.golang.org/go-maps-in-action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/doc/faq#atomic_maps"&gt;https://golang.org/doc/faq#atomic_maps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this example of a simple in memory cache the expiration was implemented...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MemoryCache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expiresSeconds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewTimer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expiresSeconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// log.Println("Timer triggered cache expiration for", key)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The error here is that the goroutine that wakes up to "expire" and remove a key/value pair from the map may contend with any other later operation (i.e. Get, Set, Delete) that is also modifying the map&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bitbucket.org/johnpfeiffer/go-cache/src"&gt;https://bitbucket.org/johnpfeiffer/go-cache/src&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A really helpful tool is to run &lt;code&gt;go test -race&lt;/code&gt; , it may take a little bit but "WARNING: DATA RACE" is pretty clear.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/race-detector"&gt;https://blog.golang.org/race-detector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="more-random-thoughts-on-concurrency-and-control"&gt;More random thoughts on concurrency and control&lt;/h3&gt;
&lt;p&gt;Controlling goroutines is like controlling threads or even any other control flow.&lt;/p&gt;
&lt;p&gt;Iterative vs Recursive calculation of factorial means either predetermined count of iterations or an indeterminate count (recursion) with a (base case) signal for termination (often called a sentinel value).&lt;/p&gt;
&lt;p&gt;So either the External Controller knows when to stop or each actor checks if it is time to stop.&lt;/p&gt;
&lt;h4 id="an-important-consideration-to-termination-is-cleanup"&gt;An important consideration to termination is cleanup&lt;/h4&gt;
&lt;p&gt;If the actor is responsible for self cleanup (as it knows what resources it is using) this can lead to resource leaks if the actor terminates unexpectedly without cleaning up.&lt;/p&gt;
&lt;p&gt;If using "dependency injection" then the Controller has knowledge of what resources were shared with the agents and can do cleanup, even if actors terminate unexpectedly.&lt;/p&gt;
&lt;p&gt;An increasingly common approach is for the Framework to facilitate cleanup so that the complexity is removed from both the Controller and the actors (e.g. Garbage Collection or Go deferred)&lt;/p&gt;
&lt;h4 id="poison-jobs-cons-and-pros"&gt;Poison Jobs cons and pros&lt;/h4&gt;
&lt;p&gt;One challenge with workers and a queue is the "poison job" which may create inefficiency or halt the system entirely as each worker who takes the job blocks/loops forever or terminates unexpectedly.&lt;/p&gt;
&lt;p&gt;One possible solution is to have a retry count so that any job which has timed out or failed and retried repeatedly is moved to a FailedJob queue (for future manual inspection and debugging) or logged and dropped entirely.&lt;/p&gt;
&lt;p&gt;Interestingly something like a "poison job" is actually a useful way to signal to concurrent actors to have an orderly termination even if they have not completed their jobs (i.e. a full system shutdown has been initiated and we want to trigger self cleanup).&lt;/p&gt;
&lt;h4 id="patterns-for-channels-and-flow-control"&gt;Patterns for Channels and Flow Control&lt;/h4&gt;
&lt;p&gt;Go Channels represent a way to map out the dependencies and then allow the compiler to optimize for parallelization.&lt;/p&gt;
&lt;p&gt;Waiting indefinitely for all goroutines to return is naive, and any termination signal must have the ability to truly interrupt work in progress, which it does NOT for goroutines, so any call that a goroutine is making MUST have a timebound where it can check for the termination signal.&lt;/p&gt;
&lt;p&gt;Therefore write your goroutines carefully knowing that you cannot cancel/aka force them to return from an infinite loop/long call, unless you exit main entirely.&lt;/p&gt;
&lt;p&gt;This means that for architecture decisions it is important to consider small separate services/applications that can provide resource usage transparency and termination control.  While this is becomes a tradeoff with coordination/orchestration complexity it is worth having modularity and clear boundaries in any application of decent complexity.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple and deterministic: fan out with predefined count, close the channel&lt;/li&gt;
&lt;li&gt;Provide timeouts and retries: a failure can occur anywhere and graceful degradation means setting limits and dealing with ephemeral errors&lt;/li&gt;
&lt;li&gt;Use the select statement for a non-blocking way to check for early application termination events&lt;/li&gt;
&lt;li&gt;Use buffers to even out spikes in work from sources/production&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://www.ardanlabs.com/blog/2017/10/the-behavior-of-channels.html"&gt;https://www.ardanlabs.com/blog/2017/10/the-behavior-of-channels.html&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="go"/><category term="golang"/><category term="goroutines"/><category term="concurrency"/><category term="channels"/><category term="select"/><category term="pipelines"/></entry><entry><title>Code is for Humans</title><link href="https://blog.john-pfeiffer.com/code-is-for-humans/" rel="alternate"/><published>2017-02-25T20:34:00-08:00</published><updated>2017-02-25T20:34:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2017-02-25:/code-is-for-humans/</id><summary type="html">
&lt;h2 id="code-is-the-automation-of-a-solution"&gt;Code is the automation of a solution&lt;/h2&gt;
&lt;p&gt;I often feel like the purpose of Programming is lost in the many discussions and debates about Programming.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Sometimes the code is conflated as being the solution.  Whatever problem is being worked on has to be thought through and different possible solutions evaluated …&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">
&lt;h2 id="code-is-the-automation-of-a-solution"&gt;Code is the automation of a solution&lt;/h2&gt;
&lt;p&gt;I often feel like the purpose of Programming is lost in the many discussions and debates about Programming.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Sometimes the code is conflated as being the solution.  Whatever problem is being worked on has to be thought through and different possible solutions evaluated.  Then one approach has to be implemented and tested.  That implementation can take many forms: &lt;a href="https://en.wikipedia.org/wiki/Turing_completeness"&gt;https://en.wikipedia.org/wiki/Turing_completeness&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Alternatively a solution is seen as perfected because it is written in code.  Yet all it takes is one more edge case, one more nuance on a variable, and the "solution" will fail.  (Bugs!)  Perhaps this is because the trivial examples of coding are often related to math, like adding two numbers, that we are somewhat misled to believe that the correct code will always solve the problem (and amplified by Computer Science which uses proofs to mathematically prove algorithmic solutions).  &lt;a href="https://en.wikipedia.org/wiki/P_versus_NP_problem"&gt;https://en.wikipedia.org/wiki/P_versus_NP_problem&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Selecting the correct algorithm assumes understanding of the problem, the constraints, and of course the end goal.  Much like picking an off-the-shelf tool or open source library or framework, there has to be some comprehension of whether their solution is your solution.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The purpose of the automation is to repeatedly solve the problem (assuming the same environment and same inputs).&lt;/p&gt;
&lt;p&gt;So first you really have to sit down and think through the solution.  Usually this involves pen (or pencil) and paper though the whiteboard is certainly one of my favorites.&lt;/p&gt;
&lt;p&gt;I certainly understand people who want to work towards solutions by coding (i.e. the IntegratedDevelopmentEnvironment and programming paradigm as just another tool for our brains) but I suspect it can also lead to distractions of accidental complexity (limitations or unfamiliarities of the programming language syntax/framework) and sometimes worse yet unnoticed biases of the tools preclude some of the best solutions.  (The infamous "imperative vs functional" debates ;)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Imperative_programming"&gt;https://en.wikipedia.org/wiki/Imperative_programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Functional_programming"&gt;https://en.wikipedia.org/wiki/Functional_programming&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Non trivial problems will require solutions that involve tradeoffs and compromises (i.e. the classic "execution time" vs "resources required").&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Software_engineering"&gt;https://en.wikipedia.org/wiki/Software_engineering&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="code-is-for-humans-not-computers"&gt;Code is for humans, not computers&lt;/h2&gt;
&lt;p&gt;Human readable code must be transformed into instructions for a machine which is what executes all of the computations.  The machine has no understanding of whether the instructions will solve the problem.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Compiler"&gt;https://en.wikipedia.org/wiki/Compiler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Machine_code"&gt;https://en.wikipedia.org/wiki/Machine_code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This fundamental impedance mismatch is one of the major challenges to programming.  Humans do not always know all of the correct instructions to provide.  Machines will faithfully execute whatever is given to them, including conflicting commands or erroneous data.&lt;/p&gt;
&lt;p&gt;So the history of the abstraction of computer programming very often reads like the evolution away from the physical hardware towards humans expressiveness because the better able we are to describe something the more likely we are to document a correct automation of a solution.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/History_of_programming_languages"&gt;https://en.wikipedia.org/wiki/History_of_programming_languages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="solving-yesterdays-problems-of-performance"&gt;Solving Yesterdays Problems of Performance&lt;/h3&gt;
&lt;p&gt;While the earliest hardwired machines filled large rooms and had different kinds of accidental complexity and bugs
&lt;a href="https://americanhistory.si.edu/collections/search/object/nmah_334663"&gt;https://americanhistory.si.edu/collections/search/object/nmah_334663&lt;/a&gt; , later generations had to deal with getting the most performance out of the machines &lt;a href="https://en.wikipedia.org/wiki/Assembly_language#Historical_perspective"&gt;https://en.wikipedia.org/wiki/Assembly_language#Historical_perspective&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These powerful low level languages can also generate some of the most persistent and pernicious bugs via manual memory management, pointers, and buffer overflows.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/C_dynamic_memory_allocation#Common_errors"&gt;https://en.wikipedia.org/wiki/C_dynamic_memory_allocation#Common_errors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Software_bug#Resource"&gt;https://en.wikipedia.org/wiki/Software_bug#Resource&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The amount of developer time required to create correct code has dramatically reduced as the speed of computation has increased and the tools (including the programming languages) are better able to "get out of the way" and avoid the accidental complexity of optimizing for performance.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is rare that the purpose of a program is to add numbers as quickly as possible&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="programming-languages-help-with-explicitness-by-removing-ambiguity-in-natural-languages"&gt;Programming languages help with explicitness by removing ambiguity in natural languages&lt;/h3&gt;
&lt;p&gt;A sentence in English, "We saw her duck", can have multiple meanings &lt;a href="https://en.wikipedia.org/wiki/Ambiguity#Linguistic_forms"&gt;https://en.wikipedia.org/wiki/Ambiguity#Linguistic_forms&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Programming languages force human expressiveness to be less ambiguous (i.e. the responsibility of the mismatch impedance of incorrect instructions falls squarely on the humans).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Names are important and should be well thought out &lt;a href="https://en.wikipedia.org/wiki/Naming_convention_(programming)"&gt;https://en.wikipedia.org/wiki/Naming_convention_(programming)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Short variables and acronyms can confuse, mislead, or misdirect other humans who will modify or extend code based on that misunderstanding&lt;/li&gt;
&lt;li&gt;Performance specific changes can become digressions and noise that distract or make brittle the tracing of the required solution logic&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="explicit-communication-because-magic-is-incomprehensible"&gt;Explicit communication because magic is incomprehensible&lt;/h2&gt;
&lt;p&gt;The problem with short meaningless variable names in unreadable code littered with performance optimizations is they prevent solving &lt;strong&gt;The Problem&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Some considerations and anti-patterns:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Straight to coding (no research)&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Real time systems that keep an aircraft in the air must pay attention to runtime constraints&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Overlooked requirements and misunderstanding the problem domain&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Not knowing what the correct answer will look like (i.e. not having test/control inputs and outputs)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Absence of acceptance tests to prove that it really is solved and the infamous "it works on my machine"&lt;/li&gt;
&lt;li&gt;The fallacy of "Perfection"; for a solution to provide value there has to be a mechanism to empirically prove it works&lt;/li&gt;
&lt;li&gt;"Done" is not "code complete" (even with tests ;) , it is Integration Tests, Acceptance Tests, Performance Tests, Soak Tests, actually shipped to the "wild" where it survives real environments and edge cases&lt;/li&gt;
&lt;li&gt;Intermittent behavior = automating of a solution should provide consistent results&lt;/li&gt;
&lt;li&gt;Ideologue = a technology looking for a problem&lt;/li&gt;
&lt;li&gt;Premature Optimization &lt;a href="https://en.wikipedia.org/wiki/Program_optimization#When_to_optimize"&gt;https://en.wikipedia.org/wiki/Program_optimization#When_to_optimize&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Premature Generalization &lt;a href="http://wiki.c2.com/?PrematureGeneralization"&gt;http://wiki.c2.com/?PrematureGeneralization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Spaghetti_code"&gt;https://en.wikipedia.org/wiki/Spaghetti_code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Big_ball_of_mud"&gt;https://en.wikipedia.org/wiki/Big_ball_of_mud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Now is the only time that matters, when actually the more successful you are the more likely the code will continue and need maintenance &lt;a href="https://en.wikipedia.org/wiki/Software_maintenance"&gt;https://en.wikipedia.org/wiki/Software_maintenance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="process-improvements-to-actually-solve-a-problem"&gt;Process Improvements to actually Solve a Problem&lt;/h2&gt;
&lt;p&gt;Considering the complexity required to actually solve a problem it would be fair to say many iterations are required.&lt;/p&gt;
&lt;p&gt;Some of the "tools" that have helped the iterative process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Software &lt;a href="https://en.wikipedia.org/wiki/Stored-program_computer#History"&gt;https://en.wikipedia.org/wiki/Stored-program_computer#History&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Testing &lt;a href="http://web.archive.org/web/20161024015955/http://www.testingreferences.com/testinghistory.php"&gt;http://web.archive.org/web/20161024015955/http://www.testingreferences.com/testinghistory.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Logging &lt;a href="https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying"&gt;https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;Automated deployments&lt;/li&gt;
&lt;li&gt;Version Control &lt;a href="http://web.archive.org/web/20170104162946/http://layervault.tumblr.com/post/102541175774/the-history-of-version-control"&gt;http://web.archive.org/web/20170104162946/http://layervault.tumblr.com/post/102541175774/the-history-of-version-control&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additionally any implementation must be bound by a "good enough" state as perfection cannot coexist with the ever changing real world.&lt;/p&gt;
&lt;p&gt;Data is required to understand progress (or regression).&lt;/p&gt;
&lt;p&gt;Humans must keep modifying until "done" (even in the advanced example of &lt;a href="https://en.wikipedia.org/wiki/Genetic_programming"&gt;https://en.wikipedia.org/wiki/Genetic_programming&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;The steady increase in computing power (&lt;a href="https://en.wikipedia.org/wiki/Moore%27s_law"&gt;https://en.wikipedia.org/wiki/Moore%27s_law&lt;/a&gt;) means &lt;strong&gt;Maintainability trumps Performance&lt;/strong&gt;.  Being able to get reproducible results was codified long ago in the &lt;a href="https://en.wikipedia.org/wiki/Scientific_method"&gt;https://en.wikipedia.org/wiki/Scientific_method&lt;/a&gt;. =]&lt;/p&gt;
&lt;h3 id="collaboration-and-crowdsourcing-without-group-think"&gt;Collaboration and Crowdsourcing without Group Think&lt;/h3&gt;
&lt;p&gt;There seems to be a strong reaction to the term "pair programming" and it is no uncommon for code reviews and pull requests to be a source of emotional angst and team friction.&lt;/p&gt;
&lt;p&gt;These techniques are well established ways of improving quality:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Pair_programming"&gt;https://en.wikipedia.org/wiki/Pair_programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.microsoft.com/en-us/research/publication/pair-programming-whats-in-it-for-me/"&gt;https://www.microsoft.com/en-us/research/publication/pair-programming-whats-in-it-for-me/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Code_review"&gt;https://en.wikipedia.org/wiki/Code_review&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Distributed_version_control#Pull_requests"&gt;https://en.wikipedia.org/wiki/Distributed_version_control#Pull_requests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While the myth of the individual genius over-emphasizes the outlier it is far more common for a group to achieve projects of any large size (even with software and technology acting as a multiplier).&lt;/p&gt;
&lt;p&gt;I am not advocating pure democracy (or just accepting status quo) as the only way of building things but it is clearly preferrable to have a variety of skills (i.e. architecture, mathematics, verification, design, etc.)&lt;/p&gt;
&lt;p&gt;It may be that working together on intellectual endeavours is currently less intuitive for humans than working together on physical challenges and that a fledgling industry that is chronically short of trained and experienced workers is not selecting and creating environments that are conducive to group working.&lt;/p&gt;
&lt;p&gt;One of the most challenging aspects is disambiguating where something has been successful in the field (mature) versus "it's always been that way" complacence.  Different isn't always better but should always be honestly evaluated.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.bloomberg.com/news/articles/2014-04-10/the-myth-of-the-lone-genius"&gt;https://www.bloomberg.com/news/articles/2014-04-10/the-myth-of-the-lone-genius&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/The_Wisdom_of_Crowds"&gt;https://en.wikipedia.org/wiki/The_Wisdom_of_Crowds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Groupthink"&gt;https://en.wikipedia.org/wiki/Groupthink&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Genetic_diversity"&gt;https://en.wikipedia.org/wiki/Genetic_diversity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="configuration-patterns"&gt;Configuration Patterns&lt;/h2&gt;
&lt;p&gt;Configuration is just another part of "Do Not Repeat Yourself" &lt;a href="https://en.wikipedia.org/wiki/Don't_repeat_yourself"&gt;https://en.wikipedia.org/wiki/Don't_repeat_yourself&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;Without configuration each program would have to be rewritten with the a new "configuration" portion hardcoded each time. (Though software is an improvement over having to build new hardware for each new configuration...)&lt;/p&gt;
&lt;p&gt;So it is a pragmatic way to extend the utility of an automated solution.&lt;/p&gt;
&lt;p&gt;"When should configurations be applied"?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configuration is passed as a parameter when the program first starts&lt;/li&gt;
&lt;li&gt;Configuration is loaded from a configuration file when the program first starts&lt;/li&gt;
&lt;li&gt;Configuration is loaded from Environment variables when the program first starts&lt;/li&gt;
&lt;li&gt;Configuration is loaded from a configuration file whenever a change to that file is detected by the program&lt;/li&gt;
&lt;li&gt;Configuration is loaded from a configuration file whenever a module is loaded (i.e. "lazy loading")&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configuration is loaded from Environment variables whenever a module is loaded (i.e. "lazy loading")&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://12factor.net/config"&gt;https://12factor.net/config&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Lazy_loading"&gt;https://en.wikipedia.org/wiki/Lazy_loading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;For a short execution time there is little difference between loading at startup versus runtime&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What are the impacts of restarting the service (in order to reload the new configuration changes)?&lt;/p&gt;
&lt;p&gt;Are there parts that have to be loaded first and then wait for a slower dependency to be available?&lt;/p&gt;
&lt;h2 id="prefer-determinism-for-reasonability"&gt;Prefer Determinism for Reasonability&lt;/h2&gt;
&lt;p&gt;Loading and initializing all configuration when the program first starts is one way of attempting to create determinism in the code paths that are running in memory.&lt;/p&gt;
&lt;p&gt;The benefit of "hot swapping" is applying changes to existing code while it is still running.&lt;/p&gt;
&lt;p&gt;The computer is not going to get confused.  It does not care if the data being passed to the module in memory is correct or incorrect, but the new dynamic result may ruin that beautifully automated solution, sometimes in almost undetectable ways.&lt;/p&gt;
&lt;p&gt;As we humans struggle with ever increasing complexity (both in the software and hardware) we should focus on how to reduce variability (that includes during coding, during compilation, and especially at run time).&lt;/p&gt;
&lt;p&gt;Some alternatives tend to take advantage of the cheaper cost of computing and increasingly distributed/networked systems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Send a reconnect signal to clients to use a new endpoint&lt;/li&gt;
&lt;li&gt;Start up a second process and have the operating system pass the network connection from the old to the new process&lt;/li&gt;
&lt;li&gt;A Load Balancer or other connection holding component that can direct traffic to the new service&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://www.martinfowler.com/bliki/BlueGreenDeployment.html"&gt;https://www.martinfowler.com/bliki/BlueGreenDeployment.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="modularity"&gt;Modularity&lt;/h2&gt;
&lt;p&gt;Smaller pieces can be easier to understand and assert for validity.  The "Do One Thing" principle helps the human who is composing a solution (ideally from re-usable components) to understand which tool is right for the job.&lt;/p&gt;
&lt;p&gt;This also allows for leveraging "seams" to investigate or decouple code (which drastically helps with maintenance).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Single_responsibility_principle"&gt;https://en.wikipedia.org/wiki/Single_responsibility_principle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://web.archive.org/web/20160803161738/http://www.informit.com/articles/article.aspx?p=359417&amp;amp;seqNum=2"&gt;http://web.archive.org/web/20160803161738/http://www.informit.com/articles/article.aspx?p=359417&amp;amp;seqNum=2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Updating, upgrading, or replacing a well defined component will be easier than something that is tightly interwoven with all of the other pieces.&lt;/p&gt;
&lt;p&gt;There is a natural tension with re-usability since something with a slight modification that can be re-used reduces the overall code footprint.  The key here is a clear understanding of whether the actual test footprint and complexity have been reduced.&lt;/p&gt;
&lt;p&gt;Also it is possible to decompose into such small parts that they have no logical coherence =[&lt;/p&gt;
&lt;h2 id="immutability"&gt;Immutability&lt;/h2&gt;
&lt;p&gt;One last technique I would like to highlight is immutability.  The idea is by preventing change it can be easier to trace and determine the expected outcome.  (Or discover the exact point at which there is an unexpected deviation).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Immutable_object"&gt;https://en.wikipedia.org/wiki/Immutable_object&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://martinfowler.com/bliki/ImmutableServer.html"&gt;https://martinfowler.com/bliki/ImmutableServer.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://web.archive.org/web/20161030171510/http://blog.codeship.com/immutable-infrastructure/"&gt;http://web.archive.org/web/20161030171510/http://blog.codeship.com/immutable-infrastructure/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It comes at a trade-off of increased resource consumption (i.e. memory) and creating an unmanageablely large number of entities (with possible corresponding orchestration or scale issues)&lt;/p&gt;
&lt;p&gt;Functional programming definitely tends towards immutability ;)&lt;/p&gt;</content><category term="programming"/><category term="programming"/><category term="readability"/><category term="immutable"/><category term="configuration"/><category term="dependencies"/></entry><entry><title>Security Encryption HTTPS OpenSSL SSH Keygen VPN Letsencrypt Certbot</title><link href="https://blog.john-pfeiffer.com/security-encryption-https-openssl-ssh-keygen-vpn-letsencrypt-certbot/" rel="alternate"/><published>2017-02-16T23:34:00-08:00</published><updated>2017-02-16T23:34:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2017-02-16:/security-encryption-https-openssl-ssh-keygen-vpn-letsencrypt-certbot/</id><summary type="html">
&lt;p&gt;As our lives become increasingly monitored and digital the privacy of being unobserved or having a private conversation that we used to be able to take for granted now requires extra effort.  The more people who choose to use these easy and readily available tools the more privacy will become …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;As our lives become increasingly monitored and digital the privacy of being unobserved or having a private conversation that we used to be able to take for granted now requires extra effort.  The more people who choose to use these easy and readily available tools the more privacy will become the standard rather than the exception.&lt;/p&gt;
&lt;h1 id="symmetric-and-asymmetric-encryption"&gt;Symmetric and Asymmetric Encryption&lt;/h1&gt;
&lt;p&gt;Using a shared secret key is generally the simplest way to encrypt, both parties use the same key to encrypt and decrypt.&lt;/p&gt;
&lt;p&gt;Asymmetric encryption (aka "public and private key") allows for a message to be encrypted without the parties having to meet or exchange a secret.&lt;/p&gt;
&lt;p&gt;Both are often used together in a complementary fashion.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Public-key_cryptography"&gt;https://en.wikipedia.org/wiki/Public-key_cryptography&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Transport_Layer_Security"&gt;https://en.wikipedia.org/wiki/Transport_Layer_Security&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Codes and the ability to communicate in secret have a very long history which many others have documented and thanks to the amazing efforts of many many people we have the ability to communicate our billions of messages with relative privacy.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/History_of_cryptography#Modern_cryptography"&gt;https://en.wikipedia.org/wiki/History_of_cryptography#Modern_cryptography&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/984428.Crypto"&gt;https://www.goodreads.com/book/show/984428.Crypto&lt;/a&gt; (Steven Levy)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is important to understand that good encryption depends on randomness to make it as hard as possible for an "attacker" to reverse engineer or guess the key, i.e. &lt;a href="https://en.wikipedia.org/wiki//dev/random"&gt;https://en.wikipedia.org/wiki//dev/random&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The following is my summary of some of the most common and useful tools...&lt;/p&gt;
&lt;h2 id="pretty-good-encryption-for-pretty-good-privacy"&gt;Pretty Good Encryption for Pretty Good Privacy&lt;/h2&gt;
&lt;p&gt;While there is a lot of value in leveraging the GPG public and private keys for authenticity checking this is just about encrypted data...&lt;/p&gt;
&lt;h3 id="gpg-encryption"&gt;GPG Encryption&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gpg -c example.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;prompts for a password to access the keyring and leverages existing (or automatically generates) public and private keys, and outputs example.tar.gz.gpg&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note: encrypting before compressing is meaningless since encrypted data is random and compression depends on repetition/patterns&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gpg --yes --passphrase=password -c example.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;non interactive encryption (if the private key is password protected), outputs example.txt.gpg&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;echo "password" | gpg --yes --no-tty --batch --passphrase-fd 0 --output encrypted.txt.gpg  --symmetric --cipher-algo AES256 plain.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;non interactive AES 256 symmetric cipher rather than public/private keypairs&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="gpg-decryption"&gt;GPG Decryption&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gpg example.tar.gz.gpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;enter the passphrase to access the private key to decrypt the file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gpg --yes --passphrase=password example.txt.gpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;non-interactively access the private key to decrypt the file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;echo "password" | gpg --yes --no-tty --batch --passphrase-fd 0 --output plain.txt --decrypt encrypted.txt.gpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;non interactively decrypt with symmetric encryption&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Do not pick password as your password ;p&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.gnupg.org/gph/en/manual/x110.html"&gt;https://www.gnupg.org/gph/en/manual/x110.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="aes-256-encryption"&gt;AES 256 Encryption&lt;/h2&gt;
&lt;p&gt;AdvancedEncryptionStandard is one of the US and world standards as an encryption algorithm.&lt;/p&gt;
&lt;p&gt;Security benefits from transparency in that if you provide the algorithm and source code in plain sight and attackers are still unable to decrypt/crack/manipulate the data then you are probably in good shape.&lt;/p&gt;
&lt;p&gt;Perhaps one of the most well known projects (open source and free!) to advance the practice of encryption is &lt;a href="https://www.openssl.org/"&gt;https://www.openssl.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here we encrypt and decrypt a text file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl aes-256-cbc -in plain.txt -out message.encrypted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;prompted for a password (symmetric key) to encrypt the file with AES 256&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl aes-256-cbc -d -in message.encrypted -out plain.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;prompted for a password to decrypt the message&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl aes-256-cbc -in plain.txt -out message.encrypted -pass pass:YOURPASSWORD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;non-interactively provide the password to encrypt the file with AES 256&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl aes-256-cbc -d -in message.encrypted -out plain.txt -pass pass:YOURPASSWORD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;non-interactively provide the password to decrypt the file with AES 256&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;an incorrect password will create a zero byte file&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl aes-256-cbc -a -d -in message.encrypted -out plain.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;if it's base64 encoded do not forget the -a&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard"&gt;https://en.wikipedia.org/wiki/Advanced_Encryption_Standard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation"&gt;https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.openssl.org/docs/manmaster/man1/openssl.html"&gt;https://www.openssl.org/docs/manmaster/man1/openssl.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="generating-ssl-certificates-and-private-and-public-keys"&gt;Generating SSL Certificates and Private and Public Keys&lt;/h1&gt;
&lt;p&gt;Perhaps the most common use of TLS/SSL are the keys used to encrypt communication with a web server, these examples use a "self signed certificate" which most libraries and browsers will not trust.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"Root Certificate Authorities" are the ones you send a "Certificate Signing Request" to generate a certificate that can be mathemetically trusted by the existing software libraries and browsers&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="more-private-and-public-key-basics-for-ssl-certificates"&gt;More Private and Public Key basics for SSL Certificates&lt;/h2&gt;
&lt;p&gt;The private key contains a series of numbers. Two of those numbers form the "public key", the others are part of your "private key".&lt;/p&gt;
&lt;p&gt;The "public key" bits are also embedded in your Certificate (we get them from your CSR - certificate signing request).&lt;/p&gt;
&lt;h3 id="verify-the-ssl-certificate-and-private-key-match"&gt;Verify the SSL Certificate and Private Key match&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(openssl x509 -noout -modulus -in server.pem | openssl md5 ; openssl rsa -noout -modulus -in server.key | openssl md5) | uniq
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;outputs a single hash if matching, if two hashes are output then it is not unique and the key and cert do not match&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To check that the public key in your cert matches the public portion of your private key:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl x509 -noout -text -in server.crt
openssl rsa -noout -text -in server.key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But since the public exponent is usually 65537 and it's challening to compare a long modulus you can use the following approach:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl x509 -noout -modulus -in server.crt | openssl md5
openssl rsa -noout -modulus -in server.key | openssl md5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the "modulus" and the "public exponent" portions of the cert and key ... aka the two md5sum hashes should match&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="verify-the-cert-and-intermediate-match-and-then-verify"&gt;Verify the Cert and Intermediate match and then Verify&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl verify -purpose sslserver -CAfile intermediate.pem -verbose server.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;ensure the intermediate certificate matches the SSL certificate (if it does not SSL trust will not work correctly)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="verify-a-csr-matches-the-key-and-certificate"&gt;Verify a CSR matches the Key and Certificate&lt;/h3&gt;
&lt;p&gt;To check to which key or certificate a particular CSR belongs you can compute&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl req -noout -modulus -in server.csr | openssl md5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="creating-keys-and-certificates-and-certificate-requests"&gt;Creating keys and certificates and certificate requests&lt;/h2&gt;
&lt;h3 id="openssl-new-key-and-cert-one-liner"&gt;openssl New Key and Cert One Liner&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl req -subj '/CN=example.com/O=My Company Name LTD./C=US' -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout server.key -out server.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;one liner to generate a self signed SSL key and certificate, since SSL certs are rotated regularly we can use 2048 instead of 4096 bits&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="openssl-certificate-commands"&gt;openssl certificate commands&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl req -out CSR.csr -new -newkey rsa:2048 -nodes -keyout privateKey.key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;create 2048 bit nopass key + csr (certificate signing request - usually sent to a Certificate Authority that is in the root chain bundled with major browsers and libraries)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl x509 -req -days 365 -in CSR.csr -signkey privateKey.key -out cert.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;self sign a CSR and generate a self signed SSL certificate&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl rsa -in mykey.pem -pubout
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;display the public key generated from the private key&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl rsa -in mykey.pem -pubout -out mykey.pub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;use the private key to generate and save the public key to a file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl req -text -noout -verify -in CSR.csr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Verify a CSR certificate request&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl rsa -check -in cert.key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Verify a key&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl x509 -text -in cert.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;View INFO about a cert and see the cert&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl x509 -noout -issuer -subject -dates -in cert.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;View specific items in the certificate (and do not print out the full certificate)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl req -out CSR.csr -key privateKey.key -new
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Generate a certificate signing request (CSR) for an existing private key&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl x509 -x509toreq -in certificate.crt -out CSR.csr -signkey privateKey.key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Generate a certificate signing request based on an existing certificate and private key&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="creating-an-rsa-certificate-with-a-password"&gt;Creating an RSA certificate with a password&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl genrsa -des3 -out domainname.key 2048
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Create a 2048 bit private key with passphrase&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl req -new -key domainname.key -out domainname.csr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;CREATE A CSR, CEERTIFICATE SIGNED REQUEST&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Common Name (domain name) = fully qualified domain name&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl x509 -req -days 365 -in CSR.csr -signkey privateKey.key -out cert.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Generate a self signed cert&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="remove-a-passphrase-to-install-a-private-key-on-a-server"&gt;Remove a passphrase to install a private key on a server&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl rsa -in domainname-passphrase.key -out domainname-server.key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;OR&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl rsa -in privateKey.pem -out newPrivateKey.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="use-openssls-built-in-webserver"&gt;Use openssl's built in webserver&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl s_server -cert server.pem -key server-nopass.key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;start a TCP server with the provided certificate and key on the default port of 4433&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl s_server -cert server.pem -accept 4433 -www
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;start an HTTPS server with the cert and key combined in a .pem on the specific port (e.g. 4433)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;telnet localhost 4433
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;verify basic network connectivity&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;wget https://localhost:4433 --no-check-certificate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;verify basic network connectivity and download the contents without validating the SSL certificate&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl s_client -showcerts -connect localhost:4433
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;show the ciphers and certificates of a server, "18 (self signed certificate)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl s_client -connect ldaps.example.com:10636 -CAfile intermediate.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;show the ciphers and certificates of a server while providing client-side the Root + Intermediate Chain certificates&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="get-a-remote-server-ssl-certificate-using-openssl-and-sed"&gt;Get a remote server ssl certificate using openssl and sed&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;echo&lt;span class="w"&gt; &lt;/span&gt;|&lt;span class="w"&gt; &lt;/span&gt;openssl&lt;span class="w"&gt; &lt;/span&gt;s_client&lt;span class="w"&gt; &lt;/span&gt;-connect&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;REMOTEHOST&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;:&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;REMOTEPORT&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;2&amp;gt;&lt;span class="err"&gt;&amp;amp;&lt;/span&gt;1&lt;span class="w"&gt; &lt;/span&gt;|&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-ne&lt;span class="w"&gt; &lt;/span&gt;'/-BEGIN&lt;span class="w"&gt; &lt;/span&gt;CERTIFICATE-/,/-END&lt;span class="w"&gt; &lt;/span&gt;CERTIFICATE-/p'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="getting-and-verifying-an-intermediate-certificate"&gt;Getting and verifying an Intermediate Certificate&lt;/h3&gt;
&lt;p&gt;Popular web browsers will often have lock symbols or other ways for you to see (and download) a certificate)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;startup vm with webserver (tomcat) + current certificate&lt;/li&gt;
&lt;li&gt;chrome browse to the https://10.10.10.199&lt;/li&gt;
&lt;li&gt;Click on the lock (certificate) symbol =&amp;gt; Certificate information  "Issued to: example.com"&lt;/li&gt;
&lt;li&gt;Click on the Certification Path (tab) ... see the root CA + intermediates + cert.&lt;/li&gt;
&lt;li&gt;click on "GeoTrust" (or the top level Certificate listed in the path) -&amp;gt; View Certificate&lt;/li&gt;
&lt;li&gt;Details (tab) -&amp;gt; Copy to File -&amp;gt; Save Base-64 encoded x.509 (.cer)&lt;/li&gt;
&lt;li&gt;Verify that it is different than your original cert.crt&lt;/li&gt;
&lt;li&gt;Copy paste that file into a place where you have openssl installed (i.e. ubuntu linux)&lt;/li&gt;
&lt;li&gt;Hostfile (/etc/hosts) so that your openssl command can use the DNS name&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openssl s_client -showcerts -connect example.com:443&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Without the intermediate you should receive "Verify return code: 21 (unable to verify the first certificate)"&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openssl s_client -showcerts -connect example.com:443 -CAfile geotrust-intermediate.crt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;With the intermediate in the command above:   "Verify return code: 0 (ok)"&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Finally, if the DNS name is publicly available, you can verify: &lt;a href="https://www.sslshopper.com/ssl-checker.html"&gt;https://www.sslshopper.com/ssl-checker.html&lt;/a&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h1 id="other-certificate-formats"&gt;Other certificate formats&lt;/h1&gt;
&lt;h2 id="convert-from-windows-iis-pkcs12-or-pfx-to-pem"&gt;CONVERT FROM WINDOWS (IIS) PKCS12 OR PFX TO PEM&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl pkcs12 -info -in keystore.p12

openssl pkcs12 -in original.pfx -out cert.pem -nodes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This creates both the key and the cert&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can copy and paste the certificate portion into cert.crt&lt;/p&gt;
&lt;p&gt;Also copy and paste the private key portion into the cert.key&lt;/p&gt;
&lt;h2 id="configure-an-existing-key-certificate-into-pkcs12"&gt;Configure an existing key + certificate into PKCS12&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;openssl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkcs12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;inkey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr/&gt;
&lt;h1 id="keytool-and-java-key-stores"&gt;KEYTOOL AND JAVA KEY STORES&lt;/h1&gt;
&lt;p&gt;Java, just like web servers, have public and private encryption keys in order to enable cryptography and encryption from within the applications.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;jvm&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sun&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.6&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.24&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;jre&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;jssecacerts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;hit enter as it has no password&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sun&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cacerts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;default password is changeit&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sun&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;changeit&lt;/span&gt;
&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jssecacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;changeit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;atmos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;n4&lt;/span&gt;


&lt;span class="n"&gt;Keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JKS&lt;/span&gt;
&lt;span class="n"&gt;Keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SUN&lt;/span&gt;

&lt;span class="n"&gt;Your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;

&lt;span class="n"&gt;Alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;digicertassuredidrootca&lt;/span&gt;
&lt;span class="n"&gt;Creation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Jan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2008&lt;/span&gt;
&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;trustedCertEntry&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="import-a-signed-primary-certificate-to-an-existing-java-keystore"&gt;Import a signed primary certificate to an existing Java keystore&lt;/h3&gt;
&lt;p&gt;One of the most common tasks is adding a certificate to trust&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;trustcacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mydomain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mydomain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sun&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;changeit&lt;/span&gt;
&lt;span class="n"&gt;Trust&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;certificate&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;

&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;

&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sun&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;changeit&lt;/span&gt;

&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;printcert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mydomain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;

&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;trustcacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;geotrust&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intermediate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sun&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;changeit&lt;/span&gt;

&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mydomain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;changeit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="extract-a-certificate-from-a-jks-into-a-der-then-convert-it-into-a-pem-and-import-into-the-jvm-cacerts"&gt;EXTRACT A CERTIFICATE FROM A JKS INTO A .DER, THEN CONVERT IT INTO A .PEM AND IMPORT INTO THE JVM cacerts&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;zanzibar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;der&lt;/span&gt;
&lt;span class="n"&gt;openssl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x509&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;der&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;inform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;outform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PEM&lt;/span&gt;

&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;trustcacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;zanzibar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sun&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;changeit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.openssl.org/docs/HOWTO/certificates.txt"&gt;http://www.openssl.org/docs/HOWTO/certificates.txt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.sslshopper.com/article-most-common-java-keytool-keystore-commands.html"&gt;http://www.sslshopper.com/article-most-common-java-keytool-keystore-commands.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h1 id="ssh-encryption"&gt;SSH Encryption&lt;/h1&gt;
&lt;p&gt;Secure Shell is for network access over an insecure network &lt;a href="https://en.wikipedia.org/wiki/Secure_Shell"&gt;https://en.wikipedia.org/wiki/Secure_Shell&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="create-a-publicprivate-key-pair-for-ssh"&gt;Create a public/private key pair for SSH&lt;/h2&gt;
&lt;p&gt;Backup any existing ~/.ssh/id_rsa  (cp -a ~./ssh ~./ssh-bak)
Backup any existing ~/.ssh/id_rsa.pub&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keygen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rsa&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;"your_email@example.com"&lt;/span&gt;
&lt;span class="n"&gt;chmod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;id_rsa&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;id_rsa and id_rsa.pub  *e.g. in /home/USERNAME/.ssh , &lt;a href="https://en.wikipedia.org/wiki/Ssh-keygen"&gt;https://en.wikipedia.org/wiki/Ssh-keygen&lt;/a&gt;
chmod to modify permissions (to the "only the owner can read") since if the private key is not restricted in security the ssh client will not run but instead return an error&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;id_rsa  IS YOUR PRIVATE KEY, GUARD IT!&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;id_rsa.pub IS THE PUBLIC PORTION WHICH YOU ADD TO REMOTE SERVERS&lt;/p&gt;
&lt;p&gt;if you add a passphrase to your SSH key (to prevent hackers from simply copying the file)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ssh-keygen -y
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;prompted for the path to the file, then prompted for the password to outpout a public signature (.pub)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ssh-keygen -t dsa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;RSA is generally preferred, protocol 2, I only include the DSA command for completeness &lt;a href="http://security.stackexchange.com/questions/5096/rsa-vs-dsa-for-ssh-authentication-keys"&gt;http://security.stackexchange.com/questions/5096/rsa-vs-dsa-for-ssh-authentication-keys&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ssh-keygen -l -f ~/.ssh/id_rsa.pub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Check the number of bits for key strength, it should at least 2048 bits&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keygen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rsa&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;"myemail@example.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;HOME&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id_rsa&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Create an RSA based key with a specific email address label as an output in a specific directory and named file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="output-a-pub-from-a-private-key"&gt;output a .pub from a private key&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ssh-keygen -y -f id_rsa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="generate-a-fingerprint-for-verification-of-a-host"&gt;generate a fingerprint for verification of a host&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ssh-keygen -lf id_rsa.pub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="amazon-ec2-instances-have-a-different-method-ec2-add-keypair"&gt;AMAZON ec2 instances have a different method, ec2-add-keypair&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl pkcs8 -in myec2key.pem -nocrypt -topk8 -outform DER | openssl sha1 -c
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="ec2-import-keypair"&gt;ec2-import-keypair:&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl pkey -in ~/.ssh/ec2/primary.pem -pubout -outform DER | openssl md5 -c
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="adding-a-public-key-to-a-remote-server"&gt;Adding a Public Key to a remote server&lt;/h2&gt;
&lt;p&gt;ON THE REMOTE SERVER IT SHOULD ONLY HAVE THE public key from .ssh/authorized_keys&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;/home/username/.ssh
sudo&lt;span class="w"&gt; &lt;/span&gt;vi&lt;span class="w"&gt; &lt;/span&gt;/home/username/.ssh/authorized_keys
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt;  &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;.ssh/id_rsa.pub&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;username@123.45.56.78&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys"&lt;/span&gt;

chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;/home/username/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Don't forget to modify:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;nano&lt;span class="w"&gt; &lt;/span&gt;/etc/ssh/sshd_config
&lt;span class="c1"&gt;#AuthorizedKeysFile     %h/.ssh/authorized_keys&lt;/span&gt;

/etc/init.d/ssh&lt;span class="w"&gt; &lt;/span&gt;force-reload
/etc/init.d/ssh&lt;span class="w"&gt; &lt;/span&gt;restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ON THE SERVER:  &lt;code&gt;ssh-agent sh -c 'ssh-add &amp;lt; /dev/null &amp;amp;&amp;amp; bash'&lt;/code&gt;
OPTIONAL?  &lt;code&gt;exec ssh-agent sh -c 'ssh-add &amp;lt;/dev/null &amp;amp;&amp;amp; exec /usr/local/bin/wmaker'&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="connecting-with-ssh-client-to-a-remote-server"&gt;Connecting with SSH client to a remote server&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id_rsa&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="mf"&gt;@123.45.56.78&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;verbose, use identity from private key&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;vvvv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id_rsa&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="nv"&gt;@example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;if errors like (nil) verify permissions and ownership (whoami=username)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Don't forget you sometimes have chmod 400&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="ssh-ignore-strict-hosts-checking"&gt;SSH ignore strict hosts checking&lt;/h3&gt;
&lt;p&gt;(i.e. developing against an FQDN with a dynamic ip, this does expose you to the improbable risk of an imposter "man in the middle" server)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ssh -i /usr/local/bamboo/bamboo_id_rsa -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;this command line parameter will not store nor verify the remote server's signature&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A more permanent configuration change:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vi ~/.ssh/config

IdentityFile ~/.ssh/id_rsa

Host example.com
        StrictHostKeyChecking no
        UserKnownHostsFile=/dev/null
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="ssh-agent-forwarding"&gt;SSH Agent Forwarding&lt;/h3&gt;
&lt;p&gt;It can be insecure to forward your SSH access through a jumpbox to a machine deeper in your network, though it is generally worse to leave SSH keyson a jumpbox.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ssh-add ~/.ssh/example.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;add a specific key to the ssh agent
    ssh-add -L
an optional step to list the keys that ssh-agent has loaded in memory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;At&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="mf"&gt;@1.2.3.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;SSH and explicitly forward the key so that the second shell session (ssh-agent) has access to it&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="mf"&gt;@5.6.7.8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the extra indentation is to indicate that from the jumpbox you are able to ssh to the internal machine&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A more permanent configuration change by using ~/.ssh/config&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Host 1.2.3.4
    ForwardAgent yes
    IdentityFile ~/.ssh/example.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://linux.die.net/man/1/ssh-add"&gt;https://linux.die.net/man/1/ssh-add&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="ssh-tcp-tunnel"&gt;SSH TCP tunnel&lt;/h3&gt;
&lt;p&gt;In this example the SSH tunnel could be used for a SOCKS proxy for the browser...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9999&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="nv"&gt;@example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;connect via ssh to a remote server, after the password prompt the process will stay open for connections locally via port 9999&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="use-a-docker-container-to-isolate-the-vpn-software-and-instead-only-allow-an-ssh-tunnel-as-a-web-proxy"&gt;Use a docker container to isolate the VPN software and instead only allow an SSH tunnel as a web proxy&lt;/h4&gt;
&lt;p&gt;An alternative to a remote machine as the SSH target for the tunnel you can instead target a docker container running locally.
- This allows you to package everything required (i.e. vpn software) into the container
- Has a security benefit that only the container (and its filesystem) are directly exposed to the VPN&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that for an even more limited security profile the container could run as an HTTP proxy as it will only handle HTTP traffic (rather than all TCP)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run --detach --rm --privileged --publish 2222:2222 ubuntu-xenial-vpn
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the container is running SSHD&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2222&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserKnownHostsFile&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="nv"&gt;@localhost&lt;/span&gt;
&lt;span class="n"&gt;openconnect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;vpn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;SSH into the container and start the openconnect vpn connection and authenticate&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9999&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2222&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserKnownHostsFile&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="nv"&gt;@localhost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="configure-firefox-to-use-the-socks-host"&gt;Configure Firefox to use the SOCKS Host&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Firefox&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Edit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Preferences&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Advanced&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Network&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Settings&lt;/span&gt;
&lt;span class="n"&gt;Manual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SOCKS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9999&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;firefox sends requests to the server over the ssh encrypted connection&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="configure-firefox-to-use-the-socks5-proxy-for-dns"&gt;Configure Firefox to use the socks5 proxy for DNS&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;about&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;
&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;socks_remote_dns&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;use the address bar to access the advanced configuration option and set socks_remote_dns true&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This ensures the browser will use the SOCKS proxy domain name server rather than the local machines&lt;/p&gt;
&lt;h3 id="ssh-tunnel-for-windows-rdp"&gt;SSH tunnel for Windows RDP&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ssh -L 3389:172.24.32.40:3389 172.24.32.100 -l sshusername -N
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;L = local port is forwarded to the remote host and port&lt;/li&gt;
&lt;li&gt;l = login_name&lt;/li&gt;
&lt;li&gt;.40 = the windows rdp server&lt;/li&gt;
&lt;li&gt;.100 = the remote ssh server which has access to the windows rdp server&lt;/li&gt;
&lt;li&gt;N = do not execute a remote command (port forwarding only)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ssh-tunneling-in-general"&gt;ssh tunneling in general&lt;/h3&gt;
&lt;p&gt;local port:host:remote-port&lt;/p&gt;
&lt;h4 id="ssh-tunnel-on-port-9090-cherokee-admin"&gt;ssh tunnel on port 9090 (cherokee-admin)&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo vi /etc/sysctl.conf
    net.ipv6.conf.all.disable_ipv6=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;** On the remote server start the admin UI (which only allows access via 127.0.0.1 by default for security reasons)**
    sudo cherokee-admin&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Cherokee&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Web&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.2.101&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Jan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2012&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Listening&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;127.0.0.1&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9090&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;TLS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IPv6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;epoll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2041&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;caching&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;O&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;single&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;Login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;User&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="k"&gt;admin&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;One&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xoKmLN0aISztVMFs&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Web&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Interface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nl"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="nl"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mf"&gt;127.0.0.1&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9090&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;


&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9090&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="nl"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9090&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="nv"&gt;@host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;L = local port is forwarded to the remote host and port, so ssh binding the localhost to port 9090&lt;/li&gt;
&lt;li&gt;ssh the remote server on port 22&lt;/li&gt;
&lt;li&gt;ssh with the specified user to the hostname (assuming DNS is correct)&lt;/li&gt;
&lt;li&gt;N = do not execute a remote command (port forwarding only)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ssh -L 9090:localhost:9090 remote_IP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;assumes the ssh port is 22 and the remote user is the same as the local user, not a good assumption&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On your browser access http://localhost:9090 to see the Cherokee Admin UI&lt;/p&gt;
&lt;p&gt;Future ideas: iptables 9090? forwarding?&lt;/p&gt;
&lt;hr/&gt;
&lt;h1 id="virtual-private-networks-and-openvpn"&gt;Virtual Private Networks and openVPN&lt;/h1&gt;
&lt;h2 id="docker-openvpn"&gt;Docker OpenVPN&lt;/h2&gt;
&lt;p&gt;Using Docker is one of the easiest ways to leverage all of the open source tools (assuming for security you inspect the upstream source code, clone the Dockerfile, build your own docker image/container ;)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# https://github.com/kylemanna/docker-openvpn&lt;/span&gt;
&lt;span class="c1"&gt;# https://openvpn.net/index.php/open-source/documentation/howto.html&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FQDN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"example.com"&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OVPN_DATA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ovpn-data"&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;volume&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OVPN_DATA&lt;/span&gt;
&lt;span class="c1"&gt;# generate the initial configuration in the volume&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OVPN_DATA&lt;/span&gt;:/etc/openvpn&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;kylemanna/openvpn&lt;span class="w"&gt; &lt;/span&gt;ovpn_genconfig&lt;span class="w"&gt; &lt;/span&gt;-u&lt;span class="w"&gt; &lt;/span&gt;udp://&lt;span class="nv"&gt;$FQDN&lt;/span&gt;
&lt;span class="c1"&gt;# generate the certificate in the volume (you must choose a passphrase)&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OVPN_DATA&lt;/span&gt;:/etc/openvpn&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;-it&lt;span class="w"&gt; &lt;/span&gt;kylemanna/openvpn&lt;span class="w"&gt; &lt;/span&gt;ovpn_initpki
&lt;span class="c1"&gt;# start the openvpn service&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OVPN_DATA&lt;/span&gt;:/etc/openvpn&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1194&lt;/span&gt;:1194/udp&lt;span class="w"&gt; &lt;/span&gt;--cap-add&lt;span class="o"&gt;=&lt;/span&gt;NET_ADMIN&lt;span class="w"&gt; &lt;/span&gt;kylemanna/openvpn

&lt;span class="c1"&gt;# generate the client certificate without the passphrase&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OVPN_DATA&lt;/span&gt;:/etc/openvpn&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;-it&lt;span class="w"&gt; &lt;/span&gt;kylemanna/openvpn&lt;span class="w"&gt; &lt;/span&gt;easyrsa&lt;span class="w"&gt; &lt;/span&gt;build-client-full&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$FQDN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nopass
&lt;span class="c1"&gt;# export the client config with embedded certificates&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OVPN_DATA&lt;/span&gt;:/etc/openvpn&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;kylemanna/openvpn&lt;span class="w"&gt; &lt;/span&gt;ovpn_getclient&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$FQDN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$FQDN&lt;/span&gt;.ovpn
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="digital-ocean-openvpn"&gt;Digital Ocean OpenVPN&lt;/h2&gt;
&lt;h3 id="dedicated-ssh-user"&gt;Dedicated SSH User&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;create a new ssh key (ops sec best practice)&lt;/li&gt;
&lt;li&gt;Setup a free Firewall with ports tcp 22 and udp 1194&lt;/li&gt;
&lt;li&gt;Create and Deploy a new droplet ($5 in a region near you for lower latency, with your newly created SSH key)&lt;/li&gt;
&lt;li&gt;Once it has finished booting use the Digital Ocean web ui: Networking -&amp;gt; Firewalls -&amp;gt; FirewallName -&amp;gt; Droplets to add the Droplet to the firewall&lt;/li&gt;
&lt;li&gt;ssh -i ~/.ssh/your-new-ssh-key root@1.2.3.4&lt;/li&gt;
&lt;li&gt;reset the root password: passwd&lt;/li&gt;
&lt;li&gt;Use the WebUI from Digital Ocean to verify you can access local console with the root user (and the new password)&lt;/li&gt;
&lt;li&gt;harden the machine by adding a dedicated ssh user (not root!)&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;useradd -s /bin/bash -m NEWUSERNAME&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;usermod -a -G admin NEWUSERNAME&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;passwd NEWUSERNAME&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;verify this with: cat /etc/passwd | grep NEWUSERNAME ; cat /etc/group | grep NEWUSERNAME&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;give the new user sudo permissions: visudo &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;UPDATE THE LINE TO: %admin ALL=(ALL) NOPASSWD:ALL&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;MOVE THE LINE BELOW: %sudo    ALL=(ALL:ALL) ALL&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;VERIFY: cat /etc/sudoers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;mkdir -p /home/NEWUSERNAME/.ssh&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;vim /home/NEWUSERNAME/.ssh/authorized_keys (paste the public key that matches the new ssh key)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;/etc/init.d/ssh force-reload&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;/etc/init.d/ssh restart&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;BE SURE that /home/NEWUSERNAME/.ssh and authorized_keys is owned by the new user (chown)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;VERIFY WITH VERBOSE the new user can SSH with: ssh -v -i ~/.ssh/your-new-ssh-key NEWUSERNAME@1.2.3.4&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;VERIFY PERMISSIONS ONCE LOGGED IN WITH SSH: sudo su&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="more-hardening"&gt;More Hardening&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;disable root user from using SSH access and harden with a non-standard port&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;vim /etc/ssh/sshd_config&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Port 22222&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;PermitRootLogin no&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;ADD TO THE END OF THE FILE: AllowUsers NEWUSERNAME&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;service sshd restart&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;VERIFY: netstat -antp  # should see 0.0.0.0:22222&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt update
apt install openvpn easy-rsa
vim /etc/sysctl.conf
    net.ipv4.ip_forward=1
sudo sysctl -p
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Setting up the certificate authority (since OpenVPN uses keys)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;make-cadir ~/openvpn-ca
cd ~/openvpn-ca
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;vim vars&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;update the export KEY_NAME, KEY_ORG, KEY_EMAIL, KEY_OU to something more personalized&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vars&lt;/span&gt;
&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ca&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;You can just press enter a bunch to use all the defaults that you preconfigured in the vars file previously&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;./build-key-server KEY_NAME&lt;/code&gt; (i.e. server or myvpn)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Just press enter to accept all the defaults again (no challenge password!) and press y at the end to Sign and then Commit the certificates&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;./build-dh
openvpn --genkey --secret keys/ta.key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;./build-key client1&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;if you want to build another one later you must use: sudo su ; cd /root/openvpn-ca ; source vars ; ./build-key client2&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="configure-openvpn"&gt;Configure openvpn&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cp&lt;span class="w"&gt; &lt;/span&gt;~/openvpn-ca/keys
sudo&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;ca.crt&lt;span class="w"&gt; &lt;/span&gt;myvpn.crt&lt;span class="w"&gt; &lt;/span&gt;myvpn.key&lt;span class="w"&gt; &lt;/span&gt;ta.key&lt;span class="w"&gt; &lt;/span&gt;dh2048.pem&lt;span class="w"&gt; &lt;/span&gt;/etc/openvpn
gunzip&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;/usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;tee&lt;span class="w"&gt; &lt;/span&gt;/etc/openvpn/myvpn.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;vim /etc/openvpn/myvpn.conf&lt;/code&gt;
    :::bash
    tls-auth ta.key 0 # Remove the preceding ; to uncomment this line
    key-direction 0
    cipher AES-256-CBC
    auth SHA256
    user nobody  # Remove the preceding ; to uncomment this line
    group nogroup
    push "redirect-gateway def1 bypass-dhcp"  # Remove the preceding ; to uncomment this line
    push "dhcp-option DNS 208.67.222.222"  # Remove the preceding ; to uncomment this line
    push "dhcp-option DNS 208.67.220.220"  # Remove the preceding ; to uncomment this line
    block-outside-dns
    cert myvpn.crt
    key myvpn.key&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;some tweaks for more security https://community.openvpn.net/openvpn/wiki/Hardening (the ta.key = tls auth)
208.67.222.222 = opendns , https://openvpn.net/index.php/open-source/documentation/howto.html&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;sudo systemctl start openvpn@myvpn&lt;/code&gt;
&lt;code&gt;sudo systemctl status openvpn@myvpn&lt;/code&gt;
&lt;code&gt;sudo systemctl enable openvpn@server&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Should end with "ovpn-myvpn[21142]: Initialization Sequence Completed"
"enable" means that the service will start at boot&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;netstat -antup&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Should list all the TCP and UDP listening services including port 1194, 0.0.0.0:1194&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;ip addr show tun0&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Should show the tunnel interface is created what the network range is (e.g. 10.8.0.1)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Consider updating myvpn.conf to use port 443 and proto tcp in order to assist clients traverse/reach the openvpn server in restricted environments&lt;/p&gt;
&lt;p&gt;TODO: automate using packer and digital ocean apis to create an image&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-an-openvpn-server-on-ubuntu-16-04"&gt;https://www.digitalocean.com/community/tutorials/how-to-set-up-an-openvpn-server-on-ubuntu-16-04&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.openwrt.org/doc/howto/vpn.openvpn#tab__using_openssl_commands_most_secure"&gt;https://wiki.openwrt.org/doc/howto/vpn.openvpn#tab__using_openssl_commands_most_secure&lt;/a&gt; (more secure by directly using openssl commands)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="connect-a-client-to-the-openvpn-server"&gt;Connect a client to the OpenVPN server&lt;/h2&gt;
&lt;h3 id="openvpn-client-config-infrastructure-on-the-remote-server"&gt;OpenVPN client config infrastructure on the remote server&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/root/client-configs/files
chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/root/client-configs/files&lt;span class="w"&gt;   &lt;/span&gt;
cp&lt;span class="w"&gt; &lt;/span&gt;/usr/share/doc/openvpn/examples/sample-config-files/client.conf&lt;span class="w"&gt; &lt;/span&gt;/root/client-configs/base.conf
vim&lt;span class="w"&gt; &lt;/span&gt;/root/client-configs/base.conf
&lt;span class="w"&gt;    &lt;/span&gt;remote&lt;span class="w"&gt; &lt;/span&gt;NEW-VPN-SERVER-IP-ADDRESS&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1194&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;nobody
&lt;span class="w"&gt;    &lt;/span&gt;group&lt;span class="w"&gt; &lt;/span&gt;nogroup
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# ca ca.crt&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# cert client.crt&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# key client.key&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;cipher&lt;span class="w"&gt; &lt;/span&gt;AES-256-CBC
&lt;span class="w"&gt;    &lt;/span&gt;auth&lt;span class="w"&gt; &lt;/span&gt;SHA256
&lt;span class="w"&gt;    &lt;/span&gt;key-direction&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;if you have modified the port and protocol ensure that 1194 and udp are updated accordingly
windows clients will not be able to downgrad the user and group to nobody/nogroup
commenting out the files as they will be embedded in the .ovpn config file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;vim /root/client-configs/make_config.sh&lt;/code&gt;
    :::bash
    KEY_DIR=/root/openvpn-ca/keys
    OUTPUT_DIR=/root/client-configs/files
    BASE_CONFIG=/root/client-configs/base.conf
    cat ${BASE_CONFIG} \
        &amp;lt;(echo -e '&lt;ca&gt;') \
        ${KEY_DIR}/ca.crt \
        &amp;lt;(echo -e '&lt;/ca&gt;\n&lt;cert&gt;') \
        ${KEY_DIR}/${1}.crt \
        &amp;lt;(echo -e '&lt;/cert&gt;\n&lt;key&gt;') \
        ${KEY_DIR}/${1}.key \
        &amp;lt;(echo -e '&lt;/key&gt;\n&lt;tls-auth&gt;') \
        ${KEY_DIR}/ta.key \
        &amp;lt;(echo -e '&lt;/tls-auth&gt;') \
        &amp;gt; ${OUTPUT_DIR}/${1}.ovpn&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This automates creating a config file via a script&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;chmod 700 make_config.sh
./make_config.sh client1
ls -ahl /root/client-configs/files/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="once-you-have-the-configuration"&gt;Once you have the configuration&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;sudo apt-get install -y openvpn&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For a linux client this installs the openvpn client software&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;scp -i ~/.ssh/VPNKEY -P 22222  NEWUSERNAME@VPN-IP-ADDRESS:client1.ovpn .&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;of course no matter what we need the open vpn client configuration&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Or... From a client computer SCP to download the $FQDN.ovpn and then connect to the openvpn server&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;openvpn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;client1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ovpn&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;verify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;following&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"/sbin/ip addr add dev tun0 local 192.168.255.6 peer 192.168.255.5"&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ifconfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;"tun0 ... inet addr:192.168.255.6"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;traffic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4145707&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m m-Double"&gt;4.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;TX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;319025&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m m-Double"&gt;319.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;KB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;checkip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amazonaws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;com&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;VPN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Wifi&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;ISP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//dnsleaktest.com/&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//whoer.net/#extended&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="import-a-ovpn-config-file-into-ubuntu-network-manager"&gt;Import a .ovpn config file into ubuntu network manager&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;sudo nmcli connection import type openvpn file myname.tcp.ovpn&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[sudo] password for your-username:
Connection 'mynameVPN' (abcd-37c175b2325d) successfully added.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="connect-to-a-vpn-from-the-cli"&gt;Connect to a VPN from the CLI&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;nmcli connection up mynameVPN&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;presuming the prior connection config was named mynameVPN&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;nmcli connection show mynameVPN&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="dns-security"&gt;DNS Security&lt;/h3&gt;
&lt;p&gt;Moreover your DNS server can "leak" or be hijacked, here are some good alternatives to your snooping ISP or "big brother" evil corp.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1.1.1.1 (cloudflare &lt;a href="https://developers.cloudflare.com/1.1.1.1/commitment-to-privacy/"&gt;https://developers.cloudflare.com/1.1.1.1/commitment-to-privacy/&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;185.121.177.177 or 169.239.202.202 (http://dnsrec.meo.ws/ part of opennic)&lt;/li&gt;
&lt;li&gt;84.200.69.80 or 84.200.70.40 (dns.watch free, neutral, privacy)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;https://www.opennic.org/&lt;/p&gt;
&lt;p&gt;echo 185.121.177.177 &amp;gt;&amp;gt; /etc/resolvconf/resolv.conf.d/tail
sudo resolvconf -u&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This should append the new resolver and force an update, though you may have to ifdown eth0 ; ifup eth0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;dig -trace example.com&lt;/code&gt;
&lt;code&gt;nslookup -debug example.com&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;either command will provide copious debugging output (and nslookup has an interactive mode with "d2" for extra debugging)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These are DNS providers that have been assimilated into the borg:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;216.146.36.36 (Dyn DNS was acquired by Oracle)&lt;/li&gt;
&lt;li&gt;209.244.0.4 (Level3 Communications telco was acquired by CenturyLink)&lt;/li&gt;
&lt;li&gt;208.67.220.220 (OpenDNS was acquired by Cisco and they basically store your data)&lt;/li&gt;
&lt;li&gt;208.69.38.205 (also opendns)&lt;/li&gt;
&lt;li&gt;8.8.8.8 (Google - so basically giving them even more data - especially when you use Chrome too)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A non standard protocol to encrypt DNS traffic: &lt;a href="https://en.wikipedia.org/wiki/DNSCrypt"&gt;https://en.wikipedia.org/wiki/DNSCrypt&lt;/a&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h1 id="letsencrypt-and-certbot-for-free-ssl-certificates"&gt;Letsencrypt and certbot for free SSL Certificates&lt;/h1&gt;
&lt;p&gt;A relatively recent development has been a widespread effort to help secure more of everyone's communications by encouraing web sites to install SSL certificates (and automate renewals) for free.&lt;/p&gt;
&lt;p&gt;Here is the tool that allows you to easily automate getting a free SSL certificate (trusted by libraries and browsers no less)...&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Once again Docker simplifies things slightly (as long as you trust the container hosted by quay.io CoreOS who was acquired by RedHat ;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Prerequisite: setup a DNS record for yourdomain.com to point to the server ip address.&lt;/p&gt;
&lt;h2 id="create-a-free-ssl-cert-with-certbot-in-docker"&gt;Create a free SSL cert with certbot in docker&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;stop&lt;span class="w"&gt; &lt;/span&gt;nginx
sudo&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-it&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;443&lt;/span&gt;:443&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;certbot&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;/etc/letsencrypt:/etc/letsencrypt&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;/var/log/letsencrypt:/var/log/letsencrypt&lt;span class="w"&gt; &lt;/span&gt;quay.io/letsencrypt/letsencrypt&lt;span class="w"&gt; &lt;/span&gt;certonly&lt;span class="w"&gt; &lt;/span&gt;--standalone&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;yourdomain.com
systemctl&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Downloads the container if it is not already in &lt;code&gt;docker images&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Starts a container with a web server that binds to port 443 &lt;/li&gt;
&lt;li&gt;The same web server/tool sends a certificate signing request (from your server running at yourdomain.com) to letsencrypt.org&lt;/li&gt;
&lt;li&gt;letsencrypt.org then attempts to contact the provided domain (DNS -&amp;gt; IP -&amp;gt; server -&amp;gt; docker container)&lt;/li&gt;
&lt;li&gt;The containerized-certbot-web-server/tool then securely downloads the new SSL certificate&lt;/li&gt;
&lt;li&gt;Files created in the process are stored in /etc/letsencrypt&lt;/li&gt;
&lt;li&gt;The "evergreen" certs are presented via symlinks in /etc/letsencrypt/live/yourdomain.com/&lt;/li&gt;
&lt;li&gt;Specific versioned copies of the certificates are stored in /etc/letsencrypt/archive/yourdomain.com&lt;/li&gt;
&lt;li&gt;letsencrypt certbot tool automatically generates the /etc/nginx/conf.d/default.conf (&lt;em&gt;which points to the config and cert files in /etc/letsencrypt/&lt;/em&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="renew-your-ssl-certificate-with-certbot-and-cron"&gt;Renew your SSL certificate with certbot and cron&lt;/h2&gt;
&lt;p&gt;You can keep renewing the certificate (which lasts 90 days) for free and there are a number of other open source tools (which leverage the API/process)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;stop&lt;span class="w"&gt; &lt;/span&gt;nginx
docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-it&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;443&lt;/span&gt;:443&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;certbot&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;/etc/letsencrypt:/etc/letsencrypt&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;/var/log/letsencrypt:/var/log/letsencrypt&lt;span class="w"&gt; &lt;/span&gt;quay.io/letsencrypt/letsencrypt&lt;span class="w"&gt; &lt;/span&gt;certonly&lt;span class="w"&gt; &lt;/span&gt;--standalone&lt;span class="w"&gt; &lt;/span&gt;--force-renewal&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;yourdomain.com
systemctl&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;you must stop the production web server briefly in order to generate the updated certificate&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you want to use a cron job for certificate renewal then put the previous instructions into an executable file named ssl-cert-renewal.sh&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'59 23 01 * * /home/USERNAME/ssl-cert-renewal.sh'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/home/USERNAME/mymonthly.cron
crontab&lt;span class="w"&gt; &lt;/span&gt;/home/USERNAME/mymonthly.cron
crontab&lt;span class="w"&gt; &lt;/span&gt;-l
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;be sure to append to mymonthly.cron as the crontab command will override any previous cron jobs&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://letsencrypt.org/how-it-works/"&gt;https://letsencrypt.org/how-it-works/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://certbot.eff.org/docs/using.html#certbot-commands"&gt;https://certbot.eff.org/docs/using.html#certbot-commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/certbot/certbot/blob/master/Dockerfile"&gt;https://github.com/certbot/certbot/blob/master/Dockerfile&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="cryptography-exercises"&gt;Cryptography Exercises&lt;/h1&gt;
&lt;p&gt;In order to really understand and enjoy cryptography you can dive deeper via some of these exercises&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cryptopals.com/"&gt;https://cryptopals.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="it"/><category term="security"/><category term="encryption"/><category term="aes"/><category term="https"/><category term="openssl"/><category term="certbot"/><category term="ssh"/><category term="keygen"/><category term="openvpn"/><category term="vpn"/><category term="letsencrypt"/></entry><entry><title>Golang JSON is challenging</title><link href="https://blog.john-pfeiffer.com/golang-json-is-challenging/" rel="alternate"/><published>2016-12-30T20:34:00-08:00</published><updated>2016-12-30T20:34:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2016-12-30:/golang-json-is-challenging/</id><summary type="html">
&lt;p&gt;Parsing JSON can be a relatively simple subject covered early in other programming languages (i.e. JavaScript ;)&lt;/p&gt;
&lt;p&gt;JSON is a really popular way to persist or transmit data, especially for APIs.  So it is really common to need to use it and yet...&lt;/p&gt;
&lt;p&gt;JSON can be surprisingly difficult in Go …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Parsing JSON can be a relatively simple subject covered early in other programming languages (i.e. JavaScript ;)&lt;/p&gt;
&lt;p&gt;JSON is a really popular way to persist or transmit data, especially for APIs.  So it is really common to need to use it and yet...&lt;/p&gt;
&lt;p&gt;JSON can be surprisingly difficult in Go because even though it is built into the language it depends already understanding a few other somewhat advanced topics.  And the challenge can be compounded by the Go philosophy of "We Did Not Put It In the Language Do It Yourself".&lt;/p&gt;
&lt;h2 id="a-quick-overview-of-json"&gt;A quick overview of JSON&lt;/h2&gt;
&lt;p&gt;JSON (I never hear humans actually say the full thing: JavaScript Object Notation , though "Jason" does get annoyed) is a way to format data structures as text and it is the modern alternative to XML.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/JSON"&gt;https://en.wikipedia.org/wiki/JSON&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://json.org/"&gt;http://json.org/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is some fuzziness about Numbers and other definitions but its beauty is definitely simplicity.&lt;/p&gt;
&lt;h2 id="why-does-go-in-json-seem-unnecessarily-advanced-or-challenging"&gt;Why does Go in JSON seem (unnecessarily) advanced or challenging?&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go with JSON requires defining/using structs for objects.  This upfront cost certainly fits the "statically compiled" model ;)  But Javascript or Python magically "just make an object" (or dictionary) which has fields/values that are very accessible.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is actually pretty common that we might not know or want to define the full (nested?) object structures we've received (as if the JSON format was for portability of data from a service outside of your control), but we're forced to figure something out...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To generically parse an object from JSON (i.e. you do not know the full structure) you must use the "empty interface" (the most generic object), and Interfaces are more advanced than simply defining structs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inferring or attempting to lazily load JSON (i.e. a mixed list of different objects - since there isn't a slice of mixed types in Go!) requires Reflection which is a relatively advanced topic for a beginning programmer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Static typing is great, except for when you're reading from JSON and you're not sure which type you should really use and you probably just want it to work simply. This impedance mismatch is natural when moving from a portable data format to a specific language and application, but it doesn't reduce the cursing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To really have the Go compiler figure out the translation between JSON to object requires "hinting" which helps with compiling Reflection magic, but starts to complicate your structs (and interfaces!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Nested Structs (fields) are the answer to Nested JSON, but then you have to really figure out how many (and lists with multiple types of objects!) and how deep you expect any nesting you'll receive. (Or just give in to your recursive desires.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pointers.  They are efficient.  Since JSON parsing can be memory intensive you will end up using them... in Nested Structs... with Interfaces... and Reflection Hints... (and since Pointers are messy and confusing there will be bugs).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;associative array &amp;lt; struct/object &amp;lt; interface &amp;lt; pointers &amp;lt; reflection&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;a completely made up ordering of complexity, arrays being the least hard to grok&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://research.swtch.com/interfaces"&gt;http://research.swtch.com/interfaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/laws-of-reflection"&gt;https://blog.golang.org/laws-of-reflection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To summarize, it is really common to get some json from somewhere from someone else and want to just peek at one field, update another field, add a key and value, and save the json.&lt;/p&gt;
&lt;p&gt;And that kind of dynamic behavior isn't inherently easy in Go. =[&lt;/p&gt;
&lt;h2 id="example-code-of-marshalling-and-unmarshalling-json-with-go"&gt;Example code of Marshalling and Unmarshalling JSON with Go&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"io"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"io/ioutil"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"log"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"os"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;logIfError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// readFile is a convenience function to read a whole file at once, LOL similar to ioutil.ReadFile()&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;totalBytes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// https://golang.org/pkg/io/ , EOF is an expected error condition&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EOF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;logIfError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// TODO: 0 bytes could be returned when not an EOF&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;totalBytes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Read %d bytes: \n%s\n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Read %d total bytes from the file\n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;totalBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// genericParsing is an example of the empty interface https://blog.golang.org/json-and-go&lt;/span&gt;
&lt;span class="c1"&gt;// https://en.wikipedia.org/wiki/JSON&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;genericParsing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;logIfError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// https://golang.org/doc/effective_go.html#interface_conversions&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.(&lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\ngeneric json parsing"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"is string:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"is int:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"is bool:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}:&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"is an array:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                                &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"    "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"is of a type I don't know how to handle"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  but I could have checked another way and found %v is a %T\n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="c1"&gt;// JSONNumber https://golang.org/pkg/encoding/json/#Decoder.UseNumber&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="c1"&gt;// http://json.org/ no floats so hinting is appreciated&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Assuming top level keys are strings, i.e. NOT [] , https://gobyexample.com/json&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rootStringsOnlyParsing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// A map of string to any type https://blog.golang.org/laws-of-reflection , http://research.swtch.com/interfaces&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;datmap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;datmap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;logIfError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nKeys are Strings in a Map:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;datmap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;datmap&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ExampleSimpleObject must be exported to parse correctly , the fields order here is used by json.Marshal output&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ExampleSimpleObject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;`json:"age"`&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:"name"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ExampleComplexObject is the magic of auto parsing, if your data never gets corrupted...&lt;/span&gt;
&lt;span class="c1"&gt;// helpful understanding of Go and JSON nesting https://eager.io/blog/go-and-json/&lt;/span&gt;
&lt;span class="c1"&gt;// hints are very powerful leveraging of Reflection that Go core libraries use for JSON&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ExampleComplexObject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;ArrayOfObjects&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;ExampleSimpleObject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:"jsonArrayOfObjects,omitempty"`&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;ArrayOfStrings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="s"&gt;`json:"jsonArrayOfStrings"`&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;JSONBoolean&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="s"&gt;`json:"jsonBoolean"`&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;JSONNumber&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="s"&gt;`json:"jsonNumber"`&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;JSONString&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s"&gt;`json:"jsonString, omitempty"`&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// jsonArrayOfNumbers is not defined and so is not included in the parsed object&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// autoUnmarshal shows Go structs making parsing JSON look easy https://golang.org/pkg/encoding/json/#example_Unmarshal&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;autoUnmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ExampleComplexObject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ExampleComplexObject&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;logIfError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Auto Unmarshal: %+v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ex&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// writeJSONFile demonstrates the power of interfaces for shared functionality&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;writeJSONFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;thing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;theJSON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MarshalIndent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"  "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;logIfError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ioutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;theJSON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mo"&gt;0644&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;logIfError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// See the omitted fields with: diff --ignore-all-space types.json output.json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// https://golang.org/pkg/os/&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;myFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ferr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"types.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;logIfError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ferr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// hint: read a file and return a slice of bytes: https://golang.org/pkg/io/ioutil/#ReadFile&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ioutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"types.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;genericParsing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;datamap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rootStringsOnlyParsing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// modifying or adding to a JSON file can be tricky&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;datamap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"injectedKey"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"injected value"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;writeJSONFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dataMapModified.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;datamap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;autoUnmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;writeJSONFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"autoUnmarshalOmits.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/json-and-go"&gt;https://blog.golang.org/json-and-go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gobyexample.com/json"&gt;https://gobyexample.com/json&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://eager.io/blog/go-and-json/"&gt;https://eager.io/blog/go-and-json/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/doc/effective_go.html#interface_conversions"&gt;https://golang.org/doc/effective_go.html#interface_conversions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://attilaolah.eu/2014/09/10/json-and-struct-composition-in-go/"&gt;http://attilaolah.eu/2014/09/10/json-and-struct-composition-in-go/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.gopheracademy.com/advent-2016/advanced-encoding-decoding/"&gt;https://blog.gopheracademy.com/advent-2016/advanced-encoding-decoding/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="common-json-gotchas-with-go"&gt;Common JSON gotchas with Go&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Oops&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:"name"`&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;`json:"timestamp"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The i int field will not be Marshaled and will therefore not exist in the JSON object&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The data structures need to be exported, otherwise you'll only end up with an empty JSON object: &lt;a href="https://play.golang.org/p/ukkjLQnSSq"&gt;https://play.golang.org/p/ukkjLQnSSq&lt;/a&gt; , &lt;a href="https://golang.org/pkg/encoding/json/#example_Unmarshal"&gt;https://golang.org/pkg/encoding/json/#example_Unmarshal&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Types are strict in Go.  JSON is unclear about "Number".  Golang will assume float64 without any hints.  Use hints, or reflection and type assertions and a magic wand... &lt;a href="https://golang.org/pkg/encoding/json/#Decoder.UseNumber"&gt;https://golang.org/pkg/encoding/json/#Decoder.UseNumber&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Marshal() returns a slice of bytes which is not a string.  so string() , &lt;a href="https://golang.org/pkg/encoding/json/#Marshal"&gt;https://golang.org/pkg/encoding/json/#Marshal&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;"The argument to Unmarshal must be a non-nil pointer", &lt;a href="https://golang.org/pkg/encoding/json/#InvalidUnmarshalError"&gt;https://golang.org/pkg/encoding/json/#InvalidUnmarshalError&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="an-example-go-json-helper-utility"&gt;An example Go JSON helper utility&lt;/h2&gt;
&lt;p&gt;The internet has many "helper" (usually performance focused) utilities/libraries for JSON with Go, here's mine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bitbucket.org/johnpfeiffer/go-jsondao/src"&gt;https://bitbucket.org/johnpfeiffer/go-jsondao/src&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;The idea is to simplify just doing minimal parsing in order to add or update a Key&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hint: RawMessage is the performance trick to not parse all of the fields.&lt;/p&gt;
&lt;h2 id="a-more-real-world-code-example-of-parsing-json-with-go"&gt;A more "real world" code example of parsing JSON with Go&lt;/h2&gt;
&lt;p&gt;I wanted to import the bookmarks from Chrome but I hadn't exported them.  I wrote this utility to parse the default chrome bookmarks json file that I did have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bitbucket.org/johnpfeiffer/bookmarks/src"&gt;https://bitbucket.org/johnpfeiffer/bookmarks/src&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="programming"/><category term="go"/><category term="golang"/><category term="json"/><category term="interfaces"/></entry><entry><title>Golang Testing Benchmark Profiling Subtests Fuzz Testing</title><link href="https://blog.john-pfeiffer.com/golang-testing-benchmark-profiling-subtests-fuzz-testing/" rel="alternate"/><published>2016-10-22T20:44:00-07:00</published><updated>2016-10-22T20:44:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2016-10-22:/golang-testing-benchmark-profiling-subtests-fuzz-testing/</id><summary type="html">
&lt;p&gt;Go makes it very easy to unit test with the packagename_test.go file right next to the package source code file(s).&lt;/p&gt;
&lt;p&gt;As a pragmatic language designed for developers who ship to production the amount of built in tooling (testing, benchmarks, etc.) is impressive.&lt;/p&gt;
&lt;p&gt;Taking an earlier example I gave …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Go makes it very easy to unit test with the packagename_test.go file right next to the package source code file(s).&lt;/p&gt;
&lt;p&gt;As a pragmatic language designed for developers who ship to production the amount of built in tooling (testing, benchmarks, etc.) is impressive.&lt;/p&gt;
&lt;p&gt;Taking an earlier example I gave of MergeSort let's examine how TestDrivenDevelopment (or Design) was used to implement it.&lt;/p&gt;
&lt;h2 id="small-functions-make-for-good-tests"&gt;Small functions make for good tests&lt;/h2&gt;
&lt;p&gt;Small functions are easier to read and code is read 1000 times more often than it is written. &lt;em&gt;(completely made up but believable fact).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Less lines of very-readable-code is usually an ok approximation for complexity, and less complexity means your program is easier to reason about (and easier to validate with tests!).&lt;/p&gt;
&lt;h3 id="some-reasons-to-not-use-mega-objects"&gt;Some reasons to not use MEGA-OBJECTS&lt;/h3&gt;
&lt;p&gt;One of the things that TDD helps focus on is modularity and requirements.  Two tensions to balance are the needs of the caller versus the needs of the function.&lt;/p&gt;
&lt;p&gt;What I mean is that the function caller wants to understand what they have to provide and what they'll get back.  If the function asks for a MEGA-OBJECT then somehow the caller has to find or create a MEGA-OBJECT (which sounds very expensive).  And if the function didn't really need the MEGA-OBJECT then the function will extract the one value it actually needs and throw all that work away.&lt;/p&gt;
&lt;p&gt;If instead the function asks for the integer primitive that is the value of the MEGA-OBJECT's this should be very easy to fulfill.  (Which is how tests help discover this MEGA-OBJECT anti-pattern, because even MEGA-OBJECT mocks are difficult).&lt;/p&gt;
&lt;p&gt;A second reason to not pass a MEGA-OBJECT is that those are usually "pass by reference" for performance reasons and if modifying/side-effects are allowed then the function may accidentally invalidate other values (or intentionally corrupt data or override permissions).&lt;/p&gt;
&lt;p&gt;The less state being passed around the easier it is to quickly write a large base of non brittle unit tests to isolate exactly where the logic goes wrong when doing "the simplest thing" and of course to communicate to others/callers how they might use your function or how it handles failure modes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://martinfowler.com/bliki/TestPyramid.html"&gt;http://martinfowler.com/bliki/TestPyramid.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html"&gt;https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="example-function-with-go-unit-tests"&gt;Example function with go unit tests&lt;/h2&gt;
&lt;p&gt;This example function requires two slices of integers.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// SlicesMerge takes two sorted slices of integers and merges them into a single sorted slice of integers&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"merged:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;a main function with print statements is the tried and true way of manual testing , &lt;code&gt;go run main.go&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="main_testgo"&gt;main_test.go&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Automated units tests means another developer can fix a bug and ensure it does not regress, read and learn expected behavior, and of course have automated continuous integration catch problems as early as possible, &lt;code&gt;go test&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"reflect"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"testing"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nilslice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TestSlicesMergeEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Parallel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;assertSlicesEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;assertSlicesEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;assertSlicesEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;assertSlicesEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TestSlicesMergeHalfEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Parallel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;assertSlicesEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;assertSlicesEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;assertSlicesEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;assertSlicesEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TestSlicesMergeNegative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// if os.Getenv("MY_ENVIRONMENT_VARIABLE") == "" {&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"skipping test: $MY_ENVIRONMENT_VARIABLE is not set"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Parallel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2870837225030527764&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;assertSlicesEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2870837225030527764&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;// Helper Functions&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;assertSlicesEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nExpected:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"\nReceived: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The idea is to have code that is testable, and just use Go code to write the tests (not another DomainSpecificLanguage to learn)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/testing/"&gt;https://golang.org/pkg/testing/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Skipping tests is fairly important to applying logic to parts of the test suite (or maybe deferring paying some technical debt)&lt;/p&gt;
&lt;p&gt;Parallel indicates the tests can run in parallel, not useful here but in larger test suites taking advantage of extra processor power (GOMAXPROCS) to speed up the feedback loop is always appreciated&lt;/p&gt;
&lt;p&gt;Even in a statically compiled language making comparisons of lists of potentially nested objects is unguaranteed, but the reflection function DeepEqual does a best effort job&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/reflect/#DeepEqual"&gt;https://golang.org/pkg/reflect/#DeepEqual&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="running-the-tests-in-sequence-and-not-parallel"&gt;Running the tests in sequence and not parallel&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;t.Parallel()&lt;/code&gt; indicates a test can run in parallel, but if you are debugging or really need a specific order then using &lt;code&gt;go test -p 1&lt;/code&gt; will force it to run each test sequentially.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/golang/go/blob/master/src/testing/testing.go#L300"&gt;https://github.com/golang/go/blob/master/src/testing/testing.go#L300&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/cmd/go/internal/test/"&gt;https://golang.org/pkg/cmd/go/internal/test/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that Go Test will execute multiple different package tests in parallel...&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;parallel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;parallel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;execution&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Parallel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;maximum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tests&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;simultaneously&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GOMAXPROCS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;Note&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;parallel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;applies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;within&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;single&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;may&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tests&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;different&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;parallel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;well&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;according&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;flag&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;see&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;help&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;h4 id="running-a-specific-test"&gt;Running a specific test&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;go test -v -run TestSlicesMergeHalf&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Getting verbose output and specifying tests is quite helpful when fixing a piece of code or test.  &lt;strong&gt;Note&lt;/strong&gt; the run parameter takes a regular expression&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="test-coverage"&gt;Test Coverage&lt;/h4&gt;
&lt;p&gt;Sometimes people talk about "test coverage" and while it's clear that 100% coverage is rarely possible (nor entirely desirable from the idea of diminishing returns and exponential growth in integration combinations outside of the simplest function) , it's still a useful metric/tool to discover if there's a chunk of code that's "whistling in the wind".&lt;/p&gt;
&lt;p&gt;&lt;code&gt;go test -cover&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"coverage: 75.0% of statements"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Generate a "coverage profile" of how many times each statement was run, use the current directory:
&lt;code&gt;go test -covermode=count -coverprofile=count.out .&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ok      command-line-arguments  0.004s  coverage: 94.7% of statements&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;go test -covermode=count -coverprofile=count.out ./stringsmoar.go ./stringsmoar_test.go&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alternatively, pass the name of the package (and test files)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;go tool cover -func=count.out&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;utilize the "coverage profile" to see the coverage breakdown by function&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;johnpfeiffer&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;stringsmoar&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;stringsmoar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;RuneFrequency&lt;/span&gt;&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;johnpfeiffer&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;stringsmoar&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;stringsmoar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;RemoveNthRune&lt;/span&gt;&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;
&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;johnpfeiffer&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;stringsmoar&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;stringsmoar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;RemoveNthItem&lt;/span&gt;&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;johnpfeiffer&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;stringsmoar&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;stringsmoar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;115&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;Permutations&lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;
&lt;span class="nl"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                                                          &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="mf"&gt;94.7&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you have a default browser configured you can use the following: &lt;code&gt;go tool cover -html=count.out&lt;/code&gt; to generate a "heat map" and see exactly how often each line of code is covered. (red means not at all ;)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/cover"&gt;https://blog.golang.org/cover&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="subtests"&gt;Subtests&lt;/h3&gt;
&lt;p&gt;Using the pattern of table driven tests improves the readability and extensibility of the "merge empty test" by applying "Don't Repeat Yourself" and removing the copy pasting of the driver function call.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"reflect"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"testing"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;assertSlicesEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nExpected:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"\nReceived: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// defining the test structure separately and clear naming helps readability&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;slicesMergeTest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TestSlicesMergeEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// alternative "anonymous struct" example &lt;/span&gt;
&lt;span class="cm"&gt;/* var testCases = [] struct {&lt;/span&gt;
&lt;span class="cm"&gt;       a        []int&lt;/span&gt;
&lt;span class="cm"&gt;       b        []int&lt;/span&gt;
&lt;span class="cm"&gt;       expected []int&lt;/span&gt;
&lt;span class="cm"&gt;   }{ */&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;testCases&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;slicesMergeTest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Without subtests&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// for _, tc := range testCases {&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//  actual := SlicesMerge(tc.a, tc.b)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//  assertSlicesEqual(t, tc.expected, actual)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// }&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;testCases&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%#v merged with %#v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;actual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;assertSlicesEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;One variation with the "subtest" feature (which may apply more to benchmarks than straightforward unit tests) is not only that a fatal will not skip subsequent tests but that the output is more verbose&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;=== RUN   TestSlicesMergeEmpty
--- PASS: TestSlicesMergeEmpty (0.00s)
PASS
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;With subtests...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;=== RUN   TestSlicesMergeEmpty
=== RUN   TestSlicesMergeEmpty/[]int{}_merged_with_[]int{}
=== RUN   TestSlicesMergeEmpty/[]int{}_merged_with_[]int(nil)
=== RUN   TestSlicesMergeEmpty/[]int(nil)_merged_with_[]int{}
=== RUN   TestSlicesMergeEmpty/[]int(nil)_merged_with_[]int(nil)
--- PASS: TestSlicesMergeEmpty (0.00s)
    --- PASS: TestSlicesMergeEmpty/[]int{}_merged_with_[]int{} (0.00s)
    --- PASS: TestSlicesMergeEmpty/[]int{}_merged_with_[]int(nil) (0.00s)
    --- PASS: TestSlicesMergeEmpty/[]int(nil)_merged_with_[]int{} (0.00s)
    --- PASS: TestSlicesMergeEmpty/[]int(nil)_merged_with_[]int(nil) (0.00s)
PASS
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Making the "table" of inputs and outputs more obvious AND the output verbosity clearer seems like a small refinement but goes a long way to making production quality testing easier&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="running-a-specific-subtest"&gt;Running a specific subtest&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;go test -v -run=TestSlicesMergeEmpty/"nil"&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gd"&gt;--- PASS: TestSlicesMergeEmpty (0.00s)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;   --- PASS: TestSlicesMergeEmpty/[]int{}_merged_with_[]int(nil) (0.00s)
&lt;span class="w"&gt; &lt;/span&gt;   --- PASS: TestSlicesMergeEmpty/[]int(nil)_merged_with_[]int{} (0.00s)
&lt;span class="w"&gt; &lt;/span&gt;   --- PASS: TestSlicesMergeEmpty/[]int(nil)_merged_with_[]int(nil) (0.00s)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Yes! You can pattern match on the string from the subtest table and only run a subset of subtests (mindblown)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;More info on the "table driven test" pattern:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/subtests"&gt;https://blog.golang.org/subtests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/golang/go/wiki/TableDrivenTests"&gt;https://github.com/golang/go/wiki/TableDrivenTests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go"&gt;http://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="benchmarking"&gt;Benchmarking&lt;/h2&gt;
&lt;p&gt;Benchmarking is most useful if you're attempting to answer a question of two variations on how to implement something.&lt;/p&gt;
&lt;p&gt;I suppose if you recorded every result and ran against exactly the same hardware you might be able to detect performance regressions, though I'd be worried about overly inconsistent/flaky results taking up way too much valuable time.&lt;/p&gt;
&lt;p&gt;Inside of a _test.go file you can also write benchmark test functions, here is one of the classic questions of "concatenating strings in Go"&lt;/p&gt;
&lt;p&gt;Here we compare the simplest concatenation of two strings and also the continued concatenation of many strings with either + or buffer.WriteString()&lt;/p&gt;
&lt;p&gt;Create myconcat_test.go and execute the following with &lt;code&gt;go test -v -bench=.&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"bytes"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"testing"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyConcatSimple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyConcatSimpleLooped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyConcatBytesBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyConcatBytesBufferLooped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;BenchmarkConcatSimple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;MyConcatSimple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;BenchmarkConcatSimpleLooped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;MyConcatSimpleLooped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;BenchmarkConcatBytesBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;MyConcatBytesBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;BenchmarkConcatBytesBufferLooped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;MyConcatBytesBufferLooped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The b.N is automatically filled in by Go until the benchmark runner is "satisfied with stability" though you can create a wrapper function for the code under test if you wish to attempt more control over iterations&lt;/p&gt;
&lt;p&gt;Remember that every benchmark is only valid against a specific set of hardware, operating system, libraries, etc. and with any changes (i.e. upgrade from Go 1.6 to 1.7) you may need to retest... unless you're just proving O(N) is better than O(N^2) ;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oh right, the results...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;BenchmarkConcatSimple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="mi"&gt;30000000&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="m m-Double"&gt;40.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt;
&lt;span class="nx"&gt;BenchmarkConcatSimpleLooped&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;20728&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt;
&lt;span class="nx"&gt;BenchmarkConcatBytesBuffer&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;5000000&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="mi"&gt;373&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt;
&lt;span class="nx"&gt;BenchmarkConcatBytesBufferLooped&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;300000&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="mi"&gt;7227&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the iterations show that the simplest naive concatentation with + is very fast for a couple of small arguments (40 nanoseconds)&lt;/p&gt;
&lt;p&gt;BUT if appending many (100+) items together buffer.Write() is better&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This example ignored all sorts of real world questions around how the strings are provided (i.e. a slice of strings might be better served by Join()) (or examine it's source code to see how they implement string concatenation!) , or memory consumption, etc.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go"&gt;http://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/hackintoshrao/daily-code-optimization-using-benchmarks-and-profiling-in-golang-gophercon-india-2016-talk-874c8b4dc3c5"&gt;https://medium.com/hackintoshrao/daily-code-optimization-using-benchmarks-and-profiling-in-golang-gophercon-india-2016-talk-874c8b4dc3c5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/strings/#Join"&gt;https://golang.org/pkg/strings/#Join&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="running-specific-benchmarks"&gt;Running specific benchmarks&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;go test -v -run=NOMATCH -bench=BenchmarkConcatSimple&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since it is using the test runner the -run= regexp not matching allows you to skip any unit tests&lt;/p&gt;
&lt;p&gt;-bench= can take a regexp to match only a subset of benchmark tests&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="go-benchmark-with-an-expensive-setup"&gt;Go Benchmark with an expensive setup&lt;/h4&gt;
&lt;p&gt;If you have some setup (e.g. creating a slice with test data) you probably do not want it inside of the benchmark ;)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;go test -v -run=NOMATCH -bench=BenchmarkKey&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;BenchmarkKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResetTimer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;problemKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the timer of the benchmark testing has been reset before the "real" load , note that the compiler is smart and this may not always be necessary&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="profiling"&gt;Profiling&lt;/h2&gt;
&lt;p&gt;TODO:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/tjholowaychuk/profiling-golang-851db2d9ae24"&gt;https://medium.com/tjholowaychuk/profiling-golang-851db2d9ae24&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.ralch.com/tutorial/golang-performance-and-memory-analysis/"&gt;http://blog.ralch.com/tutorial/golang-performance-and-memory-analysis/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dave.cheney.net/2013/07/07/introducing-profile-super-simple-profiling-for-go-programs"&gt;http://dave.cheney.net/2013/07/07/introducing-profile-super-simple-profiling-for-go-programs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/profiling-go-programs"&gt;https://blog.golang.org/profiling-go-programs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/net/http/pprof/"&gt;https://golang.org/pkg/net/http/pprof/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/runtime/pprof/"&gt;https://golang.org/pkg/runtime/pprof/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="go-fuzz-testing"&gt;Go Fuzz Testing&lt;/h2&gt;
&lt;p&gt;Fuzz testing is furthering the principle of automation (and that computers are inherently better at some things than humans) to  have software discover edge cases for tests.&lt;/p&gt;
&lt;p&gt;Basically the idea is to have software run over an extreme range or with randomness and then the edge cases that are discovered can be added into the test suite.  It has been used to enormously good effect on the Go standard library by Dmitry Vyukov.&lt;/p&gt;
&lt;p&gt;One thing you start to see when attempting to apply it is that it really a tool for helping validate handling of a specific input.  It is not a magic wand to discover bugs. ;)&lt;/p&gt;
&lt;p&gt;This kind of tool assisted exploratory testing is usually reserved for a more mature phase of a product (or in special use cases where there is high value in attempting to prove correctness).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TestSlicesMergeRandom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fuzz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;randomSeed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fuzz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;randomSeed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"random seed:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;randomSeed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;randomSeed&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;xLength&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;yLength&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xLength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;yLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;xLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fuzz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;yLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fuzz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Ints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Ints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Ints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nExpected:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"\nReceived: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Seeding randomness is part of how this gofuzz library is used; the Vyukov version actually produces output that must be parsed separately.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/google/gofuzz"&gt;https://github.com/google/gofuzz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dvyukov/go-fuzz"&gt;https://github.com/dvyukov/go-fuzz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.cloudflare.com/dns-parser-meet-go-fuzzer"&gt;https://blog.cloudflare.com/dns-parser-meet-go-fuzzer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://medium.com/@dgryski/go-fuzz-github-com-arolek-ase-3c74d5a3150c#"&gt;https://medium.com/@dgryski/go-fuzz-github-com-arolek-ase-3c74d5a3150c#&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://golang.org/pkg/math/rand/"&gt;https://golang.org/pkg/math/rand/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/sort/"&gt;https://golang.org/pkg/sort/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="programming"/><category term="go"/><category term="golang"/><category term="testing"/><category term="benchmark"/><category term="profiling"/><category term="subtests"/><category term="tdd"/></entry><entry><title>Golang Interfaces Stack Linked List Queue Map Set</title><link href="https://blog.john-pfeiffer.com/golang-interfaces-stack-linked-list-queue-map-set/" rel="alternate"/><published>2016-10-05T20:34:00-07:00</published><updated>2016-10-05T20:34:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2016-10-05:/golang-interfaces-stack-linked-list-queue-map-set/</id><summary type="html">
&lt;p&gt;Two of the most useful tools in daily professional programming are hash maps and frameworks for modular abstractions.  It's the blend of performance and composability that makes Go an attractive language for getting things done.&lt;/p&gt;
&lt;h2 id="maps-aka-hash-tables-in-go"&gt;Maps aka Hash Tables in Go&lt;/h2&gt;
&lt;p&gt;For hash tables basic operations like insert, lookup, delete …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Two of the most useful tools in daily professional programming are hash maps and frameworks for modular abstractions.  It's the blend of performance and composability that makes Go an attractive language for getting things done.&lt;/p&gt;
&lt;h2 id="maps-aka-hash-tables-in-go"&gt;Maps aka Hash Tables in Go&lt;/h2&gt;
&lt;p&gt;For hash tables basic operations like insert, lookup, delete are basically "constant time" in big O notation which makes it the "go to" default data structure for a lot of basic coding.  That helps avoid having to write and maintain all the complexity and operations of an in-memory sorted data structure.&lt;/p&gt;
&lt;p&gt;It is also often the desired data structure answer in a programming interview problem =]&lt;/p&gt;
&lt;p&gt;Here's the basics of using a map since someone else has already done all of the hard work of figuring out the correct math of the hash function to avoid collisions and the engineering behind implementing it in a performant fashion.&lt;/p&gt;
&lt;h3 id="using-a-map-in-go"&gt;Using a map in go&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;inSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// gotcha: do not depend on just the return value but use a the second value which returns in map or not&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"has value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"and is in?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// never use var m map[string]bool because m is nil and assign causes a panic&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foobar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;" elements in map"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;m2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;rune&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sc"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the two main gotchas for maps in go are to not accidentally assume that the value coming back from a map means that the key was in the map since if the key is not in the map it will return the default "empty" value which will be 0 or false, and to not start with a nil pointer since later assignment will cause a panic&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/fqvNrELy1S"&gt;https://play.golang.org/p/fqvNrELy1S&lt;/a&gt; &lt;em&gt;play with maps yourself&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.m.wikipedia.org/wiki/Hash_table"&gt;https://en.m.wikipedia.org/wiki/Hash_table&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/go-maps-in-action"&gt;https://blog.golang.org/go-maps-in-action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/src/runtime/hashmap.go#L9"&gt;https://golang.org/src/runtime/hashmap.go#L9&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="sets"&gt;Sets&lt;/h3&gt;
&lt;p&gt;A common question in coding is whether a certain key or object has already been seen before.  The lookup cost in a set is  cheaper than a full search through a more complex data structure like a binary tree.&lt;/p&gt;
&lt;p&gt;Golang currently does not have a "set" built into the language but given the code example you can see how to quickly achieve the same functionality with a map.  This is a common pattern: they keep the language simple and focus on low level components that perform and compose well while we code exactly what we need for a given usage.  (i.e. a huge meta "YouAren'tGoingToNeedIt")&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Set_(abstract_data_type)"&gt;https://en.wikipedia.org/wiki/Set_(abstract_data_type)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://xlinux.nist.gov/dads/HTML/set.html"&gt;https://xlinux.nist.gov/dads/HTML/set.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/golang/go/wiki/MethodSets"&gt;https://github.com/golang/go/wiki/MethodSets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="low-memory-footprint-map-as-a-set"&gt;Low memory footprint map as a set&lt;/h4&gt;
&lt;p&gt;When implementing a Set, in some cases you can be very parsimonious with memory by using an empty struct.
This means you will rely on the very explicit _, ok pattern of checking for presence in the set.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"unsafe"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;rune&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}{}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%#v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unsafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unsafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int8&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unsafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unsafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;An empty struct is 0, a boolean and an int8 are size 1 (byte), an interface is size 8&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/9cUE9-wwDY"&gt;https://play.golang.org/p/9cUE9-wwDY&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/unsafe/#Sizeof"&gt;https://golang.org/pkg/unsafe/#Sizeof&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="polymorphism-with-go-interfaces"&gt;Polymorphism with Go Interfaces&lt;/h2&gt;
&lt;p&gt;While definitely a distinctly different approach on objects and polymorphism than Object Oriented languages like Java and Python, the critical capabilities of Interfaces allows flexibility and re-usability in functionality enabling things like the Strategy Pattern.&lt;/p&gt;
&lt;p&gt;Starting with the fundamentals of Types and Structs, Interfaces are a natural extension to separating "What" behavior is desired versus "How" it is implemented.&lt;/p&gt;
&lt;p&gt;Using Interfaces can feel tricky at first but really the main challenge is the distinction between pointer receiver methods vs the default that Go has of pass by value (considering a pointer to be an integer value of an address ;)&lt;/p&gt;
&lt;p&gt;Also, testing in Go relies heavily on the developer paying that up front cost of creating Interfaces (which is a much better longer term modular abstraction) rather than using Mocks.&lt;/p&gt;
&lt;p&gt;This example uses a trivial Stack data structure but implements it 3 different ways to illustrate how the interface can have a variety of implementations and the caller can decide which one they prefer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Stacker is a data structure that has specific data access and storage properties&lt;/span&gt;
&lt;span class="c1"&gt;// also known as a Last In First Out queue, and not fully implemented for this example =]&lt;/span&gt;
&lt;span class="c1"&gt;// This interfaces only requires two methods to implement&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stacker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// DemoStack shows a demo example of different implementations of the interface&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DemoStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stacker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pushing 2, 1, 0, onto"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"contains: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// FakeStack implements the interface using only value receivers&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;FakeStack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fakeStackCheater&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;

&lt;span class="c1"&gt;// Show returns the global slice&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;FakeStack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fakeStackCheater&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Push uses a global variable (evil) so it can get away with a value method receiver&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;FakeStack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fakeStackCheater&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fakeStackCheater&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// SliceStack is an integer stack data structure built using a slice&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SliceStack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Show displays the contents of the stack, pass a copy as no modification needed&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SliceStack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Push an integer onto the stack, pass a reference so the receiver method can directly modify&lt;/span&gt;
&lt;span class="c1"&gt;// https://github.com/golang/go/wiki/CodeReviewComments#receiver-type&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;SliceStack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* A real stack based upon a linked list */&lt;/span&gt;

&lt;span class="c1"&gt;// IntNode is a pointer data structure for holding an integer&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IntNode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;IntNode&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;IntNode&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// LinkedListStack is an integer stack data structure built using a slice&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LinkedListStack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;IntNode&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Show returns the total number of elements currently stored in the LinkedListStack&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LinkedListStack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Push an integer onto the stack, pass a reference so the receiver method can directly modify&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;LinkedListStack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IntNode&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// a fake stack does not use pointer method receivers and must modify the data structure some other way&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;FakeStack&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;DemoStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"FakeStack"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// stacks that modify their underlying data structure must pass a reference&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SliceStack&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;DemoStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"SliceStack"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LinkedListStack&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;DemoStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"LinkedListStack"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This example contradicts one of the best practices: pointer receivers should be consistent &lt;a href="https://tour.golang.org/methods/4"&gt;https://tour.golang.org/methods/4&lt;/a&gt; , so Show() and Push() should have the same receiver type&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;A Pointer Receiver allows the method to make a modification (i.e. syntactic sugar where &lt;code&gt;(s *SliceStack) Push(n int)&lt;/code&gt; could be thought of as &lt;code&gt;Push(s *SliceStack, n int)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A Pointer Receiver prevents an extra copy (in the instance of a very large object)&lt;/li&gt;
&lt;li&gt;All of the methods of an interface should be consistent (as a part of the developer user experience)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/U1l2Ni89L4"&gt;https://play.golang.org/p/U1l2Ni89L4&lt;/a&gt; &lt;em&gt;play along with the source code snippet&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bitbucket.org/johnpfeiffer/go-interfaces-stack-linkedlist"&gt;https://bitbucket.org/johnpfeiffer/go-interfaces-stack-linkedlist&lt;/a&gt; &lt;em&gt;the more complete source code&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Stack_(abstract_data_type)"&gt;https://en.wikipedia.org/wiki/Stack_(abstract_data_type)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/doc/effective_go.html#interfaces"&gt;https://golang.org/doc/effective_go.html#interfaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/golang/go/wiki/CodeReviewComments#receiver-type"&gt;https://github.com/golang/go/wiki/CodeReviewComments#receiver-type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Polymorphism_(computer_science)"&gt;https://en.wikipedia.org/wiki/Polymorphism_(computer_science)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Strategy_pattern"&gt;https://en.wikipedia.org/wiki/Strategy_pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nathanleclaire.com/blog/2015/10/10/interfaces-and-composition-for-effective-unit-testing-in-golang/"&gt;https://nathanleclaire.com/blog/2015/10/10/interfaces-and-composition-for-effective-unit-testing-in-golang/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.cloudflare.com/go-interfaces-make-test-stubbing-easy/"&gt;https://blog.cloudflare.com/go-interfaces-make-test-stubbing-easy/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="doubly-linked-list-and-first-in-first-out-queue"&gt;Doubly Linked List and First In First Out Queue&lt;/h2&gt;
&lt;p&gt;With some small additions the linked list can be enhanced to provide the functionality of a Queue.
The "doubly linked list" (&lt;a href="https://en.wikipedia.org/wiki/Doubly_linked_list"&gt;https://en.wikipedia.org/wiki/Doubly_linked_list&lt;/a&gt;) means that one can traverse from either the head (using next) or the tail (using previous).
It is not too expensive to add the extra previous pointer to each node and a tail pointer and this makes the FIFO capabilities fairly straightforward.
&lt;em&gt;Thankfully with a garbage collected language like Go we do not have to worry about manually allocating or deallocating memory, though we should always keep an eye out for memory leaks&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// IntNode is a pointer data structure for holding an integer&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IntNode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;previous&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;IntNode&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;IntNode&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// MyList is a linked list of pointers&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyList&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;IntNode&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;tail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;IntNode&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Enqueue adds an integer onto the end of the list&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;MyList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IntNode&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Dequeue removes the first integer added to the list (from the front)&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;MyList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Dequeue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// TODO: error handling for dequeuing when the list is empty&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyList&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FirstInFirstOut head: %v at memory address %p \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FirstInFirstOut tail: %v at memory address %p \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dequeuing a value: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Dequeue&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FirstInFirstOut head: %v at memory address %p \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FirstInFirstOut tail: %v at memory address %p \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The terminology changed a little bit from Push to Enqueue but now we can have a simple "fair buffer"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/tHIiRsk443C"&gt;https://play.golang.org/p/tHIiRsk443C&lt;/a&gt; &lt;em&gt;your queue in play&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Queue_(abstract_data_type)"&gt;https://en.wikipedia.org/wiki/Queue_(abstract_data_type)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)"&gt;https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cs.cmu.edu/~adamchik/15-121/lectures/Stacks%20and%20Queues/Stacks%20and%20Queues.html"&gt;https://www.cs.cmu.edu/~adamchik/15-121/lectures/Stacks%20and%20Queues/Stacks%20and%20Queues.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="doubly-linked-list-in-the-go-standard-library"&gt;Doubly Linked List in the Go Standard Library&lt;/h3&gt;
&lt;p&gt;Wonderfully there is an implementation of a Doubly Linked List in the Go standard library: &lt;a href="https://golang.org/pkg/container/list/"&gt;https://golang.org/pkg/container/list/&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"container/list"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;e1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PushBack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// enqueue&lt;/span&gt;
&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InsertAfter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;
&lt;span class="nx"&gt;e3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Back&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InsertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;displayFIFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nTraverse in reverse with Prev(): "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;displayLIFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;L2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;L2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PushBack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"D"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;displayFIFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;L2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PushBackList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;L2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;displayFIFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nRemoved %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;displayFIFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nFront is now: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Front&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n%#v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;displayFIFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nList length: %v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Len&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Front&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%v "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;displayLIFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nList length: %v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Len&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Back&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Prev&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;All of the node and pointer implementation is already handled for you, play with it here: &lt;a href="https://play.golang.org/p/FUEtqMNoaP9"&gt;https://play.golang.org/p/FUEtqMNoaP9&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content><category term="programming"/><category term="go"/><category term="golang"/><category term="interfaces"/><category term="stack"/><category term="linked list"/><category term="queue"/><category term="map"/><category term="set"/><category term="strategy pattern"/></entry><entry><title>Golang Slices Functions Filters Mergesort</title><link href="https://blog.john-pfeiffer.com/golang-slices-functions-filters-mergesort/" rel="alternate"/><published>2016-10-02T23:45:00-07:00</published><updated>2016-10-02T23:45:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2016-10-02:/golang-slices-functions-filters-mergesort/</id><summary type="html">
&lt;h2 id="tips-on-learning-a-new-programming-language"&gt;Tips on learning a new programming language&lt;/h2&gt;
&lt;p&gt;Learning a new programming language takes a lot of effort.  Here are some tips I've found to make it easier:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Layering: read through the material and just accept things without trying to understand every piece, small pieces are easier to digest and absorb …&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">
&lt;h2 id="tips-on-learning-a-new-programming-language"&gt;Tips on learning a new programming language&lt;/h2&gt;
&lt;p&gt;Learning a new programming language takes a lot of effort.  Here are some tips I've found to make it easier:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Layering: read through the material and just accept things without trying to understand every piece, small pieces are easier to digest and absorb&lt;/li&gt;
&lt;li&gt;Repetition: it is natural to read and re-read before comprehension occurs, learning right before sleep is scientifically proven to better enter long term memory&lt;/li&gt;
&lt;li&gt;Immersion: surround yourself with reminders of the material like books, web sites, on your phone, flash cards, online and real life discussions, videos&lt;/li&gt;
&lt;li&gt;Variety: sometimes it sticks better with a book, other times a video or podcast.  Different teachers will emphasize or convey certain topics better.  Do not give up because it's confusing from one source, there are many many sources to learn from&lt;/li&gt;
&lt;li&gt;Practice: apply yourself as programming is far better internalized "in action" rather than just theory&lt;/li&gt;
&lt;li&gt;Engage: using provided exercises will get you to the solution the instructor expects, creating your own coding projects will force you to use the tools in ways that make sense to you (and discover why certain things are "best practice")&lt;/li&gt;
&lt;li&gt;Go Deep: once comfortable moving around, real fluency and understanding requires digging into exactly how something is architected and implemented, what are the corner cases, and why things are done one way over another&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="arrays-underlying-slices-with-memory-addresses-and-unsafe-pointers"&gt;Arrays underlying slices with memory addresses and unsafe pointers&lt;/h2&gt;
&lt;p&gt;Arrays are a mainstay of programming (mechanical sympathy for memory addresses and our very serial brains).
Here is a major digression into Go slices which is a pointer structure that holds an array and manages the dynamic resizing.
&lt;em&gt;Hint: the whole point of Go is to not need to use low level unsafe pointer arithmetic&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"unsafe"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;capacityDoubles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;capacityDoubles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// slice of zero length and zero capacity, basically just a "slice header"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// NOTE this is different from a "nil slice" which is a nil pointer&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nilPointer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"example nil pointer: %#v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nilPointer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"has memory adddress: %p \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nilPointer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"example empty slice: %#v \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"has memory adddress: %p \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"both have the same length:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nilPointer&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"but does each equal nil? "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nilPointer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// capacity doubles so a new underlying array, if we checked with reflect the memory address would change&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"len=%d cap=%d %v\n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// https://golang.org/pkg/reflect/#SliceHeader&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// since a slice is a struct containing an underlying array of memory addresses...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// https://golang.org/pkg/builtin/#byte&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Slice of byte memory addresses: %p "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// 0xc42006e200 example address of the slice&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%p "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%p "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// 0xc420074458 0xc420074459 , the two elements are exactly 1 byte apart in address locations&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Getting the value from the memory location using a piont&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nThe byte value in the second element is binary five: %b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// https://golang.org/pkg/unsafe/&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nUnsafe pointer of the first memory location: %p"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;unsafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Pointer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// unsafe pointer arithmetic&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;lastAddress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;uintptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unsafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Pointer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;unsafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// convert back into a usable pointer type&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;lastAddressPtr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;unsafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Pointer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastAddress&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nAfter pointer arithmetic and unsafe re-typed: %p, value: %b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lastAddressPtr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;lastAddressPtr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nSafe examination of the second location in the slice: %#v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;has&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;adddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xc42000e440&lt;/span&gt;
&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="nx"&gt;has&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;adddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xc42000e4a0&lt;/span&gt;
&lt;span class="nx"&gt;both&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;same&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;but&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;does&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;Slice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xc42000e580&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xc42000a618&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xc42000a619&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="nx"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;second&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;binary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;five&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;
&lt;span class="nx"&gt;Unsafe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pointer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xc42000a618&lt;/span&gt;
&lt;span class="nx"&gt;after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pointer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;arithmetic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;unsafe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;re&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xc42000a619&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;
&lt;span class="nx"&gt;Safe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;examination&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;second&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;uint8&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="mh"&gt;0xc42000a619&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Reminder, just because we can does not mean we should =p&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/QaGtbfPwkS"&gt;https://play.golang.org/p/QaGtbfPwkS&lt;/a&gt; &lt;em&gt;(you can try it yourself!)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.golang.org/go-slices-usage-and-internals"&gt;https://blog.golang.org/go-slices-usage-and-internals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/reflect/#SliceHeader"&gt;https://golang.org/pkg/reflect/#SliceHeader&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/unsafe/"&gt;https://golang.org/pkg/unsafe/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Dereference_operator"&gt;https://en.wikipedia.org/wiki/Dereference_operator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dave.cheney.net/2018/07/12/slices-from-the-ground-up"&gt;https://dave.cheney.net/2018/07/12/slices-from-the-ground-up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="examples-of-go-slice-operations-and-tricks"&gt;Examples of Go Slice operations and tricks&lt;/h3&gt;
&lt;p&gt;Some examples of slices in action &lt;a href="https://github.com/golang/go/wiki/SliceTricks"&gt;https://github.com/golang/go/wiki/SliceTricks&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 1, 2, 3, 2&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// pre-allocating might be premature optimization and lead to bugs&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;premature&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;premature&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;premature&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;premature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;premature&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;premature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// "11 [foo          bar]"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"multiple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"once"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// "5 [add multiple items at once]"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// COPY also known as ADD to a slice (in this case add to a nil slice)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// the triple dots (ellipsis in english) indicates a variadic parameter&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// https://golang.org/ref/spec#Passing_arguments_to_..._parameters&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// https://golang.org/src/builtin/builtin.go?s=4716:4763#L124&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// "5 [add multiple items at once]"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// perhaps more readable&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// "5 [add multiple items at once]"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// CUT - but warning, this only removes it from the slice... &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// NOT the underlying array so a possible memory leak!&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// up to but not including index 1, start at index 4 to the end&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// "2 [add once]"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// DELETE index 2 (same ordering)...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// so no memory leak by correctly setting it to the zero value (usually nil for objects)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// "4 [add multiple at once]"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// INSERT into index 2 a new value&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// b = append(b[:2], append([]string{"foobar"}, b[2:]...)...)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Preferred more readable and avoid the extra slice creation with copy sleight of hand&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"foobar"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// "5 [add multiple foobar at once]"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// SWAP two values&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// "5 [multiple add foobar at once]"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// REVERSE by using the mirror image effect and multiple assignments&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;opp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;opp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;opp&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// "5 [once at foobar add multiple]"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Slices are fairly fundamental and while most things are easy there are definitely some gotchas&lt;/p&gt;
&lt;p&gt;A critical thing to remember when reasoning is that slices are references to underlying arrays, so small subslice from a very large slice will prevent that larger object/array from being garbage collected&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/znwrmQavmn"&gt;https://play.golang.org/p/znwrmQavmn&lt;/a&gt; (work with the slice tricks example yourself)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/ref/spec#Passing_arguments_to_..._parameters"&gt;https://golang.org/ref/spec#Passing_arguments_to_..._parameters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/src/builtin/builtin.go?s=4716:4763#L124"&gt;https://golang.org/src/builtin/builtin.go?s=4716:4763#L124&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Of course, read the docs and test it for your requirements, situation, and circumstances!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="gotcha-with-slices-and-pointers"&gt;Gotcha with Slices and Pointers&lt;/h3&gt;
&lt;p&gt;Assigning a new slice variable (pointer) to an existing slice will link the two together.  This may lead to undesired "side-effects".&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// [a b c]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// [a c]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// [a c c]&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%p \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%p \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;removing (cut or delete) a specific element in the slice using the new pointer affects the original slice&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://play.golang.org/p/togSAlnj6J"&gt;https://play.golang.org/p/togSAlnj6J&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="functions-anonymous-functions-functions-as-parameters-and-filters"&gt;Functions, Anonymous Functions, Functions as Parameters, and Filters&lt;/h2&gt;
&lt;p&gt;Go has functions as first class citizens.  Just assign a function to a variable or define a type that is a function signature.
Since go starts from simple blocks we build, as needed, more complex tools like filtering from a slice of integers.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// https://golang.org/doc/codewalk/functions/&lt;/span&gt;
&lt;span class="c1"&gt;// filter is a type that allows us to apply a test to the provided integer parameter&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;

&lt;span class="c1"&gt;// applyFilter is a trivial examply of using a filter as a parameter&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;applyFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// SliceFilter returns a new slice filtered using the filter function parameter&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SliceFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// isEven returns a filter (the anonymous function inside)&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;isEven&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;iseven&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2 is even: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;iseven&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"4 is even: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;applyFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iseven&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"is even: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SliceFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iseven&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// [2 4 6] is even:  [2 4 6]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"is not even: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SliceFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iseven&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// [1 3 7 9] is not even:  []&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"filtered for evens becomes: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SliceFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isEven&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// [5 3 -1 12] filtered for evens becomes:  [12]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;With a function that returns a function, and a function that requires a parameter that is of the type "function signature", we can apply the Strategy Pattern&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/Uxm7HZzS-V"&gt;https://play.golang.org/p/Uxm7HZzS-V&lt;/a&gt; &lt;em&gt;(yes you can mess with functions too)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Anonymous_function"&gt;https://en.wikipedia.org/wiki/Anonymous_function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Strategy_pattern"&gt;https://en.wikipedia.org/wiki/Strategy_pattern&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mergesort"&gt;MergeSort&lt;/h2&gt;
&lt;p&gt;And because I like source code in blogs, a highly imperfect mergesort.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// SliceSplit is a function to split a slice into roughly even partitions&lt;/span&gt;
&lt;span class="c1"&gt;// https://golang.org/doc/effective_go.html#two_dimensional_slices&lt;/span&gt;
&lt;span class="cm"&gt;/* A more specific implementation than SliceSplit with the special case partitionSize = 1 could have been&lt;/span&gt;
&lt;span class="cm"&gt;result := make([][]int, len(n), len(n))&lt;/span&gt;
&lt;span class="cm"&gt;for i := 0; i &amp;lt; len(n); i++ {&lt;/span&gt;
&lt;span class="cm"&gt;    element := make([]int, 1, 1)&lt;/span&gt;
&lt;span class="cm"&gt;    element[0] = n[i]&lt;/span&gt;
&lt;span class="cm"&gt;    result[i] = element&lt;/span&gt;
&lt;span class="cm"&gt;}&lt;/span&gt;
&lt;span class="cm"&gt;return result&lt;/span&gt;
&lt;span class="cm"&gt;*/&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SliceSplit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([][]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// TODO: is there a better way of handling split into 0 pieces?&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cannot split length %d into %d pieces"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;partitionSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;partitionSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;partitionSize&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// special case to pad the last partition with all elements&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// SlicesMerge takes two sorted slices of integers and merges them into a single sorted slice of integers&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;bi&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//MergeConsecutiveElements joins two consecutive slice elements together&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MergeConsecutiveElements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SlicesMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// MergeSort uses the merge sort algorithm to return a sorted a slice of integers&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MergeSort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SliceSplit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MergeConsecutiveElements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MergeConsecutiveElements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unsorted start:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sorted:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MergeSort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unsorted start:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sorted:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MergeSort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unsorted start:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sorted:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MergeSort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unsorted start:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sorted:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MergeSort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Merge_sort"&gt;https://en.wikipedia.org/wiki/Merge_sort&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://play.golang.org/p/nAHF50zk7m"&gt;https://play.golang.org/p/nAHF50zk7m&lt;/a&gt; &lt;em&gt;(modify the merge online)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bitbucket.org/johnpfeiffer/go-slice-mergesort"&gt;https://bitbucket.org/johnpfeiffer/go-slice-mergesort&lt;/a&gt; &lt;em&gt;(because version control means the fun never stops)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="programming"/><category term="go"/><category term="golang"/><category term="testing"/><category term="arrays"/><category term="slices"/><category term="functions"/><category term="filter"/><category term="algorithms"/><category term="merge"/><category term="mergesort"/><category term="strategy pattern"/></entry><entry><title>Consul Service Discovery and Cluster Configuration</title><link href="https://blog.john-pfeiffer.com/consul-service-discovery-and-cluster-configuration/" rel="alternate"/><published>2016-08-27T21:01:00-07:00</published><updated>2016-08-27T21:01:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2016-08-27:/consul-service-discovery-and-cluster-configuration/</id><summary type="html">
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Basically consul is an out-of-the-box service discovery system intended for clustered and highly available applications.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.consul.io/intro/"&gt;https://www.consul.io/intro/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.consul.io/docs/internals/jepsen.html"&gt;https://www.consul.io/docs/internals/jepsen.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This kind of infrastructure simplifies the programming of distributed systems so that it is easier to deliver value quickly on the …&lt;/p&gt;</summary><content type="html">
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Basically consul is an out-of-the-box service discovery system intended for clustered and highly available applications.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.consul.io/intro/"&gt;https://www.consul.io/intro/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.consul.io/docs/internals/jepsen.html"&gt;https://www.consul.io/docs/internals/jepsen.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This kind of infrastructure simplifies the programming of distributed systems so that it is easier to deliver value quickly on the actual domain problems.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I have certainly done my fair share of hardcoded config files to "discover" dependency services and even used chef for "config management"...
But with the evolution of dev-ops, web scale, microservices, containers, etc. it is great to leverage an existing battle tested solution&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="consul-cluster-using-docker"&gt;Consul Cluster using Docker&lt;/h2&gt;
&lt;p&gt;Following the straightforward work from this Docker Image we can run a cluster on a single machine:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://hub.docker.com/r/progrium/consul/"&gt;https://hub.docker.com/r/progrium/consul/&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;su
docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;node1&lt;span class="w"&gt; &lt;/span&gt;-h&lt;span class="w"&gt; &lt;/span&gt;node1&lt;span class="w"&gt; &lt;/span&gt;progrium/consul&lt;span class="w"&gt; &lt;/span&gt;-server&lt;span class="w"&gt; &lt;/span&gt;-bootstrap-expect&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;
&lt;span class="nv"&gt;JOIN_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;inspect&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{{.NetworkSettings.IPAddress}}'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;node1&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;node2&lt;span class="w"&gt; &lt;/span&gt;-h&lt;span class="w"&gt; &lt;/span&gt;node2&lt;span class="w"&gt; &lt;/span&gt;progrium/consul&lt;span class="w"&gt; &lt;/span&gt;-server&lt;span class="w"&gt; &lt;/span&gt;-join&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$JOIN_IP&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;node3&lt;span class="w"&gt; &lt;/span&gt;-h&lt;span class="w"&gt; &lt;/span&gt;node3&lt;span class="w"&gt; &lt;/span&gt;progrium/consul&lt;span class="w"&gt; &lt;/span&gt;-server&lt;span class="w"&gt; &lt;/span&gt;-join&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$JOIN_IP&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8400&lt;/span&gt;:8400&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8500&lt;/span&gt;:8500&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8600&lt;/span&gt;:53/udp&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;node4&lt;span class="w"&gt; &lt;/span&gt;-h&lt;span class="w"&gt; &lt;/span&gt;node4&lt;span class="w"&gt; &lt;/span&gt;progrium/consul&lt;span class="w"&gt; &lt;/span&gt;-join&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$JOIN_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The second 2 nodes join the first one in the cluster by using the inspected IP Address,
the last container is a consul agent (not in the quorum) but has public ports for interactivity&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl&lt;span class="w"&gt; &lt;/span&gt;localhost:8500/v1/catalog/nodes
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"Node"&lt;/span&gt;:&lt;span class="s2"&gt;"node1"&lt;/span&gt;,&lt;span class="s2"&gt;"Address"&lt;/span&gt;:&lt;span class="s2"&gt;"172.17.0.2"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Node"&lt;/span&gt;:&lt;span class="s2"&gt;"node2"&lt;/span&gt;,&lt;span class="s2"&gt;"Address"&lt;/span&gt;:&lt;span class="s2"&gt;"172.17.0.3"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Node"&lt;/span&gt;:&lt;span class="s2"&gt;"node3"&lt;/span&gt;,&lt;span class="s2"&gt;"Address"&lt;/span&gt;:&lt;span class="s2"&gt;"172.17.0.4"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Node"&lt;/span&gt;:&lt;span class="s2"&gt;"node4"&lt;/span&gt;,&lt;span class="s2"&gt;"Address"&lt;/span&gt;:&lt;span class="s2"&gt;"172.17.0.5"&lt;/span&gt;&lt;span class="o"&gt;}]&lt;/span&gt;
dig&lt;span class="w"&gt; &lt;/span&gt;@0.0.0.0&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8600&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;node1.node.consul
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;QUESTION&lt;span class="w"&gt; &lt;/span&gt;SECTION:
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;node1.node.consul.&lt;span class="w"&gt;     &lt;/span&gt;IN&lt;span class="w"&gt;  &lt;/span&gt;ANY

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ANSWER&lt;span class="w"&gt; &lt;/span&gt;SECTION:
&lt;span class="w"&gt;    &lt;/span&gt;node1.node.consul.&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;IN&lt;span class="w"&gt;  &lt;/span&gt;A&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;172&lt;/span&gt;.17.0.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;REST API call to the list of nodes, then DNS client to get the Record for the first node&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl&lt;span class="w"&gt; &lt;/span&gt;http://localhost:8500/v1/status/leader
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"172.17.0.2:8300"&lt;/span&gt;
curl&lt;span class="w"&gt; &lt;/span&gt;http://localhost:8500/v1/status/peers
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"172.17.0.2:8300"&lt;/span&gt;,&lt;span class="s2"&gt;"172.17.0.3:8300"&lt;/span&gt;,&lt;span class="s2"&gt;"172.17.0.4:8300"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

curl&lt;span class="w"&gt; &lt;/span&gt;http://localhost:8500/v1/health/node/node1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;some more REST calls about the basic nodes, RAFT leadership and peers, and node health&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.consul.io/docs/agent/http.html"&gt;https://www.consul.io/docs/agent/http.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.consul.io/docs/agent/dns.html"&gt;https://www.consul.io/docs/agent/dns.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;:::bash&lt;/p&gt;
&lt;p&gt;curl http://localhost:8500/v1/catalog/services
    {"consul":[]}
curl http://localhost:8500/v1/catalog/service/web
    []&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Listing of the services available, no web service yet =)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="alternative-install-from-zip"&gt;Alternative Install from Zip&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="err"&gt;­&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;unzip&lt;/span&gt;
&lt;span class="nx"&gt;wget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//releases.hashicorp.com/consul/0.7.0/consul_0.7.0_linux_amd64.zip&lt;/span&gt;
&lt;span class="nx"&gt;unzip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;consul_0&lt;/span&gt;&lt;span class="m m-Double"&gt;.7.0&lt;/span&gt;&lt;span class="nx"&gt;_linux_amd64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zip&lt;/span&gt;
&lt;span class="nx"&gt;BINDIP&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ifconfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eth0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"inet addr"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;':'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;­&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;­&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;­&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;consul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;­&lt;/span&gt;&lt;span class="nx"&gt;bootstrap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;­&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;­&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;BINDIP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;­&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;­&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;consul&lt;/span&gt;

&lt;span class="nx"&gt;netstat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;­&lt;/span&gt;&lt;span class="nx"&gt;antp&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;consul&lt;/span&gt;
&lt;span class="nx"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:8500/v1/status/peers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Note getting the IP Address on ubuntu 16.04 uses enp3s0 or enp25 which can be changed back via grub workaround: GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.consul.io/docs/agent/options.html"&gt;https://www.consul.io/docs/agent/options.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="registering-a-service"&gt;Registering a Service&lt;/h2&gt;
&lt;p&gt;Creating and running a very simplistic golang web server (assuming you have go installed ;) , &lt;code&gt;go run web.go&lt;/code&gt;
&lt;a href="https://blog.john-pfeiffer.com/go-programming-intro-with-vs-code-and-arrays-slices-functions-and-testing/"&gt;https://blog.john-pfeiffer.com/go-programming-intro-with-vs-code-and-arrays-slices-functions-and-testing/&lt;/a&gt;
(though you could also use nginx in docker)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"hi\n"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;From the previous steps we should have an Agent where we can register the new web service...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

curl&lt;span class="w"&gt; &lt;/span&gt;--header&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-X&lt;span class="w"&gt; &lt;/span&gt;PUT&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="s1"&gt;'{&lt;/span&gt;
&lt;span class="s1"&gt;  "ID": "web1",&lt;/span&gt;
&lt;span class="s1"&gt;  "Name": "web",&lt;/span&gt;
&lt;span class="s1"&gt;  "Tags": [&lt;/span&gt;
&lt;span class="s1"&gt;    "master",&lt;/span&gt;
&lt;span class="s1"&gt;    "v1"&lt;/span&gt;
&lt;span class="s1"&gt;  ],&lt;/span&gt;
&lt;span class="s1"&gt;  "Address": "127.0.0.1",&lt;/span&gt;
&lt;span class="s1"&gt;  "Port": 8080,&lt;/span&gt;
&lt;span class="s1"&gt;  "EnableTagOverride": false,&lt;/span&gt;
&lt;span class="s1"&gt;  "Check": {&lt;/span&gt;
&lt;span class="s1"&gt;    "HTTP": "http://localhost:8080/health",&lt;/span&gt;
&lt;span class="s1"&gt;    "Interval": "10s",&lt;/span&gt;
&lt;span class="s1"&gt;    "TTL": "15s"&lt;/span&gt;
&lt;span class="s1"&gt;  }&lt;/span&gt;
&lt;span class="s1"&gt;}'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;http://localhost:8500/v1/agent/service/register
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In order to verify the new service is registered (besides the 200 response code)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl http://localhost:8500/v1/catalog/services
    {"consul":[],"web":["master","v1"]}
curl http://localhost:8500/v1/health/service/web
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Our new service is created and doing well&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So many more things can be done with &lt;a href="https://www.consul.io/docs/agent/http/agent.html#agent_service_register"&gt;https://www.consul.io/docs/agent/http/agent.html#agent_service_register&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Stopping the web server (control + C) and checking that Consul has noticed Status is critical |o/&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl&lt;span class="w"&gt; &lt;/span&gt;http://localhost:8500/v1/health/checks/web
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"Node"&lt;/span&gt;:&lt;span class="s2"&gt;"node4"&lt;/span&gt;,&lt;span class="s2"&gt;"CheckID"&lt;/span&gt;:&lt;span class="s2"&gt;"service:web1"&lt;/span&gt;,&lt;span class="s2"&gt;"Name"&lt;/span&gt;:&lt;span class="s2"&gt;"Service 'web' check"&lt;/span&gt;,&lt;span class="s2"&gt;"Status"&lt;/span&gt;:&lt;span class="s2"&gt;"critical"&lt;/span&gt;,&lt;span class="s2"&gt;"Notes"&lt;/span&gt;:&lt;span class="s2"&gt;""&lt;/span&gt;,&lt;span class="s2"&gt;"Output"&lt;/span&gt;:&lt;span class="s2"&gt;"TTL expired"&lt;/span&gt;,&lt;span class="s2"&gt;"ServiceID"&lt;/span&gt;:&lt;span class="s2"&gt;"web1"&lt;/span&gt;,&lt;span class="s2"&gt;"ServiceName"&lt;/span&gt;:&lt;span class="s2"&gt;"web"&lt;/span&gt;&lt;span class="o"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Starting the web server again and check&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl&lt;span class="w"&gt; &lt;/span&gt;http://localhost:8500/v1/health/service/web
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"Node"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Node"&lt;/span&gt;:&lt;span class="s2"&gt;"node4"&lt;/span&gt;,&lt;span class="s2"&gt;"Address"&lt;/span&gt;:&lt;span class="s2"&gt;"172.17.0.5"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"Service"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"ID"&lt;/span&gt;:&lt;span class="s2"&gt;"web1"&lt;/span&gt;,&lt;span class="s2"&gt;"Service"&lt;/span&gt;:&lt;span class="s2"&gt;"web"&lt;/span&gt;,&lt;span class="s2"&gt;"Tags"&lt;/span&gt;:&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"master"&lt;/span&gt;,&lt;span class="s2"&gt;"v1"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="s2"&gt;"Address"&lt;/span&gt;:&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;,&lt;span class="s2"&gt;"Port"&lt;/span&gt;:8080&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"Checks"&lt;/span&gt;:&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"Node"&lt;/span&gt;:&lt;span class="s2"&gt;"node4"&lt;/span&gt;,&lt;span class="s2"&gt;"CheckID"&lt;/span&gt;:&lt;span class="s2"&gt;"service:web1"&lt;/span&gt;,&lt;span class="s2"&gt;"Name"&lt;/span&gt;:&lt;span class="s2"&gt;"Service 'web'check"&lt;/span&gt;,&lt;span class="s2"&gt;"Status"&lt;/span&gt;:&lt;span class="s2"&gt;"critical"&lt;/span&gt;,
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"Notes"&lt;/span&gt;:&lt;span class="s2"&gt;""&lt;/span&gt;,&lt;span class="s2"&gt;"Output"&lt;/span&gt;:&lt;span class="s2"&gt;"TTL expired"&lt;/span&gt;,&lt;span class="s2"&gt;"ServiceID"&lt;/span&gt;:&lt;span class="s2"&gt;"web1"&lt;/span&gt;,&lt;span class="s2"&gt;"ServiceName"&lt;/span&gt;:&lt;span class="s2"&gt;"web"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Node"&lt;/span&gt;:&lt;span class="s2"&gt;"node4"&lt;/span&gt;,&lt;span class="s2"&gt;"CheckID"&lt;/span&gt;:&lt;span class="s2"&gt;"serfHealth"&lt;/span&gt;,&lt;span class="s2"&gt;"Name"&lt;/span&gt;:&lt;span class="s2"&gt;"Serf Health Status"&lt;/span&gt;,&lt;span class="s2"&gt;"Status"&lt;/span&gt;:&lt;span class="s2"&gt;"passing"&lt;/span&gt;,&lt;span class="s2"&gt;"Notes"&lt;/span&gt;:&lt;span class="s2"&gt;""&lt;/span&gt;,&lt;span class="s2"&gt;"Output"&lt;/span&gt;:&lt;span class="s2"&gt;"Agent alive and reachable"&lt;/span&gt;,&lt;span class="s2"&gt;"ServiceID"&lt;/span&gt;:&lt;span class="s2"&gt;""&lt;/span&gt;,&lt;span class="s2"&gt;"ServiceName"&lt;/span&gt;:&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="o"&gt;}]}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="redis-in-containers-as-another-service"&gt;Redis in Containers as another Service&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run --rm -it -p 0.0.0.0:6379:6379 --name redis redis:alpine

docker run --rm -it --link redis:redis redis:alpine redis-cli -h redis -p 6379 help keys
docker run --rm -it --entrypoint=/bin/sh --link redis:redis redis:alpine
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;By running a local redis service we can modify our simple Go web service to query consul and dynamically discover how to reach the correct dependency, "look mom, no config files!"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/redis/"&gt;https://hub.docker.com/_/redis/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="distributed-configuration-and-the-go-client-library"&gt;Distributed Configuration and the Go Client Library&lt;/h2&gt;
&lt;p&gt;A simple use case is to use the key value store to distribute other information besides services that need to be discovered.&lt;/p&gt;
&lt;p&gt;Obviously interacting directly with Consul as a client from inside the application is beneficial to "keeping it all in the code" and not relying on config files or shell scripts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.consul.io/docs/agent/http/kv.html"&gt;https://www.consul.io/docs/agent/http/kv.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hashicorp/consul/tree/master/api"&gt;https://github.com/hashicorp/consul/tree/master/api&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://godoc.org/github.com/hashicorp/consul/api"&gt;https://godoc.org/github.com/hashicorp/consul/api&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Documentation on the Key Value store and the official Go client library&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="python-client"&gt;Python Client&lt;/h3&gt;
&lt;p&gt;Using an open source client can help avoid "do not repeat yourself" of writing the REST API wrapper (and benefiting from crowd source at work)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://consulate.readthedocs.io/en/stable/"&gt;http://consulate.readthedocs.io/en/stable/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/gmr/consulate"&gt;https://github.com/gmr/consulate&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;sudo pip3 install consulate&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Like all open source projects this has some bugs and outstanding PRs but it is better than another one I tried which was still in alpha (aka not really fully implemented) , &lt;a href="https://www.consul.io/downloads_tools.html"&gt;https://www.consul.io/downloads_tools.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="some-gotchas"&gt;Some Gotchas&lt;/h2&gt;
&lt;p&gt;Consul has a few edge cases that you may need to address specifically:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If a node reboots and changes ip address it will not go well: &lt;a href="https://github.com/hashicorp/consul/issues/457"&gt;https://github.com/hashicorp/consul/issues/457&lt;/a&gt; , the simplest case might be to just remove it's data directory and force it to rejoin without any data&lt;/li&gt;
&lt;li&gt;If a new node attempts to join a cluster it needs to know the ip address of an existing node, there is no "auto discovery-join" mechanism except to delegate to Atlas, the paid SaaS product from HashiCorp, or of course to write your own workaround &lt;a href="https://www.consul.io/docs/guides/bootstrapping.html"&gt;https://www.consul.io/docs/guides/bootstrapping.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If all of the server nodes in the cluster go down then there is no auto-recovery (which is not surprising I suppose...) &lt;a href="https://www.consul.io/docs/guides/outage.html"&gt;https://www.consul.io/docs/guides/outage.html&lt;/a&gt; , &lt;a href="https://github.com/hashicorp/consul/issues/454"&gt;https://github.com/hashicorp/consul/issues/454&lt;/a&gt; , &lt;a href="https://github.com/hashicorp/consul/issues/526"&gt;https://github.com/hashicorp/consul/issues/526&lt;/a&gt;, again, if you write your own wrapper to detect this scenario as the nodes reboot (or in an immutable world are re-added assuming you have solved #1 and #2 ;) they "should" be able to recover and reload from raft/peers.json&lt;/li&gt;
&lt;/ol&gt;</content><category term="build-CI-CD-devops"/><category term="service discovery"/><category term="consul"/><category term="cluster"/><category term="clustering"/><category term="config"/></entry><entry><title>A micro story about migrating a personal monolith into microservices</title><link href="https://blog.john-pfeiffer.com/a-micro-story-about-migrating-a-personal-monolith-into-microservices/" rel="alternate"/><published>2016-07-29T20:59:00-07:00</published><updated>2016-07-29T20:59:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2016-07-29:/a-micro-story-about-migrating-a-personal-monolith-into-microservices/</id><summary type="html">
&lt;p&gt;I'm a fan of best practices (who isn't?) and as complexity increases one of the modern paradigms is to use microservices to more transparently manage complexity, reduce tight coupling, and decrease maintenance overhead.&lt;/p&gt;
&lt;p&gt;Taking a different approach to my "less than webscale" personal web services has still made great improvements …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;I'm a fan of best practices (who isn't?) and as complexity increases one of the modern paradigms is to use microservices to more transparently manage complexity, reduce tight coupling, and decrease maintenance overhead.&lt;/p&gt;
&lt;p&gt;Taking a different approach to my "less than webscale" personal web services has still made great improvements to the services and my quality of life.&lt;/p&gt;
&lt;p&gt;This article is categorized as Build and CI/CD and DevOps because while microservices is transformative for larger organizations to avoid the negative consequences of Conway's Law and provides a better architecture for really complex systems, in my case it was simply about decoupling deployments and not having all of my eggs in one basket. (e.g. the Drupal migration below actually did not affect the User Experience in any visible way).&lt;/p&gt;
&lt;h2 id="how-to-grow-a-monolith"&gt;How to grow a monolith&lt;/h2&gt;
&lt;h3 id="value-value-value"&gt;Value, value, value&lt;/h3&gt;
&lt;p&gt;I've had a Virtual Private Server for many years and have been always able to find something for free (or as my requirements increased at most $10 a month).  This has been at least 1000x worth the investment as it has provided a platform for learning and experimenting with all sorts of technology (Linux, PHP, Python, Twisted, MySQL, Redis, etc.) and of course a useful catch all tool (to check DNS resolution or temporarily store a file).&lt;/p&gt;
&lt;p&gt;I'm a firm believer in understanding the whole stack.  Maintaining the OS and managing deployments has made me conscious of the many hidden costs and compromises/trade-offs in software and services.&lt;/p&gt;
&lt;h3 id="organic-sprawl"&gt;Organic Sprawl&lt;/h3&gt;
&lt;p&gt;While I started with a personal notes site in PHP, I eventually added a couple of personal blogs using Drupal, and then another Drupal site about physics (&lt;a href="http://physicstime.com"&gt;http://physicstime.com&lt;/a&gt;) for my father in law, and then a few experiments in Python (including uwsgi and Twisted).  This was all of course underpinned by the shared underlying Cherokee web server, Drupal, PHP, Python, MySQL, and Ubuntu.&lt;/p&gt;
&lt;p&gt;Physicstime alone has served over 500,000 visitors =)&lt;/p&gt;
&lt;p&gt;I envisioned that having a common platform would make it easier to add more sites and the shared maintenance meant I would only have to pay it once, especially considering the alternative of paying for and deploying many separate servers.&lt;/p&gt;
&lt;p&gt;I got to try a lot of different technologies but it started to be clear that it was not a "common platform" and maintenance (and just the mental energy of worrying about deferred maintenance) started to take up way too much time and effort.&lt;/p&gt;
&lt;h3 id="the-downsides-of-a-monolith"&gt;The downsides of a monolith&lt;/h3&gt;
&lt;p&gt;Anecdotes: a security update meant the infrastructure provider needed to restart all of their host machines which meant "hmmm, will all my services restart correctly on reboot?" - luckily yes.  Then I had to patch my virtual server (anything on the Internet is one vulnerability away from becoming taken over and in the least case being used for spam/DDoS and eventually being unable to actually serve my sites, in the worst case rooted or keystroke-logged in an attempt to hack my life or in a serious criminal pursuit).&lt;/p&gt;
&lt;p&gt;Oh and then there's the times the logs (web or auth due to anonymous attackers scanning) or backups filled the disk... (facepalm)&lt;/p&gt;
&lt;p&gt;Unavailability Due To:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;any infrastructure vendor maintenance&lt;/li&gt;
&lt;li&gt;any OS maintenance/upgrade&lt;/li&gt;
&lt;li&gt;incorrectly configured/rogue application&lt;/li&gt;
&lt;li&gt;a security issue in one affects them all&lt;/li&gt;
&lt;li&gt;they all contend for the same resources&lt;/li&gt;
&lt;li&gt;they all share the same version/dependency requiring upgrading and testing everything at once&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="in-context-linode-ubuntu-cherokee-and-drupal-were-good-choices-at-the-time"&gt;In Context: Linode, Ubuntu, Cherokee, and Drupal were good choices at the time&lt;/h3&gt;
&lt;p&gt;Firstly let me say I picked a great vendor (Linode) who was very solid (they limit bad actor customers which tends to make resources predictable) and that Ubuntu OS and Cherokee webserver are very easy to setup and maintain which is one reason why I've put this off for so long.&lt;/p&gt;
&lt;p&gt;Drupal does an ok job of separating the tech stack from the content publishing so it was possible to ignore the tech side for awhile.&lt;/p&gt;
&lt;p&gt;Another factor is that iPhones/Android, Dropbox, Bitbucket/GitHub, PaaS, and a whole generation of technologies were not around when I set this up.&lt;/p&gt;
&lt;p&gt;Finally, maybe it's a corollary to Moore's law and the prevalence of the cloud but there's quite a bit of free compute around than there used to be =]&lt;/p&gt;
&lt;h2 id="thinking-microservices"&gt;Thinking Microservices&lt;/h2&gt;
&lt;p&gt;Thinking about microservices is like TDD (Test Driven Design): it exposes assumptions, unmanaged organic evolution, and accidental complexity.&lt;/p&gt;
&lt;h3 id="discovering-the-real-problem-domain"&gt;Discovering the real problem domain&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;When you have a hammer everything looks like a nail&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It becomes too easy to just use an interesting or popular technology for everything regardless of the true problem.&lt;/p&gt;
&lt;p&gt;Analyzing what I actually did with the various services I realized there were actually two distinct phases: creation and distribution.&lt;/p&gt;
&lt;p&gt;I did not have a "realtime" or "high volume dynamic data" use case, nor even a large number of content publishers that needed extra tooling.  (The plugins/additions I used weren't even that exotic.)&lt;/p&gt;
&lt;p&gt;In contrast the overhead was my irreplaceable time spent for the maintenance of Ubuntu patching/upgrades, including the underlying PHP, Drupal upgrades, backups, and of course the inestimable risk of running something on the Internet. =p&lt;/p&gt;
&lt;h2 id="how-i-converted-to-microservices-big-bang-vs-kanban"&gt;How I Converted to Microservices: Big Bang vs Kanban&lt;/h2&gt;
&lt;p&gt;A common question is "how", and since "it depends" ;) in this case I had limited free hours to accomplish change and a strong desire to not break existing service.&lt;/p&gt;
&lt;p&gt;Rather than "Big Bang" I went Kanban &lt;a href="https://en.m.wikipedia.org/wiki/Kanban_(development)"&gt;https://en.m.wikipedia.org/wiki/Kanban_(development)&lt;/a&gt; (a much better analogy than Martin Fowler's "strangler pattern" analogy &lt;a href="http://www.martinfowler.com/bliki/StranglerApplication.html"&gt;http://www.martinfowler.com/bliki/StranglerApplication.html&lt;/a&gt;).  This allowed gradual migration with the least disruption and the most flexibility in when changes would occur.  As a not-to-be-underestimated bonus it was also the least stressful.&lt;/p&gt;
&lt;p&gt;Since I publish a post maybe once a month my blog was the simplest place to start.&lt;/p&gt;
&lt;p&gt;Feature parity requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;publish read-only pages to the world&lt;/li&gt;
&lt;li&gt;editing&lt;/li&gt;
&lt;li&gt;some minimal data structure (category and tags)&lt;/li&gt;
&lt;li&gt;search&lt;/li&gt;
&lt;li&gt;preserve content and meta data&lt;/li&gt;
&lt;li&gt;regular backup of the content&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I migrated my content to a Pelican static site (with advanced functionality via a nice JavaScript theme) running on GitHub static pages and Travis CI.&lt;/p&gt;
&lt;p&gt;Having text in git provides a built in backup (published as html and stored remotely in markdown in the free "Software as a Service" repository) with of course a local git repo working copy on my laptop.&lt;/p&gt;
&lt;p&gt;The result is I only need to do a single git push of markdown text to deploy to production.  It's highly available, scalable, and as a bonus there is versioning and I got a fairly snazzy facelift with improved search.&lt;/p&gt;
&lt;h3 id="migration-has-extra-costs-of-research-and-tweaks-but-is-an-opportunity-for-new-benefits"&gt;Migration has extra costs of research and tweaks but is an opportunity for new benefits&lt;/h3&gt;
&lt;p&gt;First I had to research and evaluate static site generators and static site hosts.&lt;/p&gt;
&lt;p&gt;Next I had to learn how to setup a workflow with test data to automate the process from writing content to publishing.&lt;/p&gt;
&lt;p&gt;The actual data migration was quite lengthy.  This was due to the inevitable format change (export from Drupal to .md) and post transformation validation. &lt;/p&gt;
&lt;p&gt;Of course I also had to update DNS entries and even setup subdomains and 301 redirects.&lt;/p&gt;
&lt;p&gt;Like any rewrite I also ended up adding things (like tags).&lt;/p&gt;
&lt;p&gt;One of the most beneficial "while I'm already redoing everything anyways" enhancements was adding CDN and SSL via Cloudflare which actually added another layer of availability and security.&lt;/p&gt;
&lt;p&gt;Cloudflare also has very useful feature for blocking noise with Security -&amp;gt; Security Rules -&amp;gt; Create rule , to block "Country is in RU, CN" or "AS Num is in 16276, 14061, 24940" (OVH, DigitalOcean, Hetzner"&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.cloudflare.com/learning/network-layer/what-is-an-autonomous-system/"&gt;https://www.cloudflare.com/learning/network-layer/what-is-an-autonomous-system/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="data-gravity-is-expensive-and-microservices-allows-polyglot-so-go-programming"&gt;Data Gravity is expensive and Microservices allows polyglot so Go Programming&lt;/h3&gt;
&lt;p&gt;My personal notes are full of years of research and was the most time consuming, fulfilling the adages of "data gravity" and "unstructured data costs you" =(&lt;/p&gt;
&lt;p&gt;A free static site on bitbucket.org (markdown in a free git repository transformed to html for free via Shippable.com) used the same pattern (and Pelican tech) as my blog: version control, offloading the hosting to someone else, and JavaScript for search.&lt;/p&gt;
&lt;p&gt;While the file system organically captures metadata like "created date" I had to inject that into the content; I found this to be a data integrity improvement as I had noticed before that FTP and git have a tendency to discard that metadata.&lt;/p&gt;
&lt;p&gt;One of the most time consuming transformations was just tweaking the plain text into markdown but this was worth the improvement in readability since the content is far more often read than written =]&lt;/p&gt;
&lt;p&gt;While Markdown violates the principle of separating data and presentation I found it to be a pragmatic compromise as it IS a standard and it's machine readable. (I could theoretically use a script to convert it back to plaintext ;)&lt;/p&gt;
&lt;p&gt;Oh right, so Go, aka Golang?&lt;/p&gt;
&lt;p&gt;I leveraged Google AppEngine and for fun got to use the relatively new programming language Go to write a custom 301 redirector to prevent links on the Internet from breaking and allow search engines link from all of the previous URLs to the new locations.&lt;/p&gt;
&lt;p&gt;While Python is very easy to pickup I found Go to be similar enough (especially to C) to be also not so hard to learn (lots of documentation!) and better able to do what I needed simply within the language (though Python pretty much has a library for everything it also has C dependencies that don't always play nice with a PlatformAsAService).&lt;/p&gt;
&lt;h3 id="drupal-to-drupal"&gt;Drupal to Drupal&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The more things change the more they stay the same.&lt;/p&gt;
&lt;p&gt;Wherever you go there you are.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The most active Drupal site will stay Drupal on DigitalOcean (to leverage their one click example) and cheaper prices.&lt;/p&gt;
&lt;p&gt;Edit: one click was for Drupal8, yet another headache migration, so not in the scope of this project.&lt;/p&gt;
&lt;p&gt;Now at least as an isolated service (website) on a dedicated server, updates will be specific to it.&lt;/p&gt;
&lt;p&gt;I also put in the effort to use automation via Packer and experiment with Docker...&lt;/p&gt;
&lt;h4 id="immutable-packer"&gt;Immutable Packer&lt;/h4&gt;
&lt;p&gt;I considered Docker Machine and Ansible but both seemed the wrong tools for my purpose.&lt;/p&gt;
&lt;p&gt;Docker Machine is still relatively new and is more oriented towards a cluster of nodes.  Additionally the post docker image phase (ssh commands to install things) seems overly complex.&lt;/p&gt;
&lt;p&gt;Ansible (SSH paradigm) is simpler than chef but encourages a mutable long lived server.&lt;/p&gt;
&lt;p&gt;Packer has a simple and straight forward way of building an immutable server image for DigitalOcean yet retains the flexibility to adapt to other cloud vendors later if needed.&lt;/p&gt;
&lt;h4 id="drupal-website-context-and-domain-problems"&gt;Drupal website context and domain problems&lt;/h4&gt;
&lt;p&gt;Besides the basic components (Docker will simplify this): nginx, php, MySQL&lt;/p&gt;
&lt;p&gt;The ongoing issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Backups of the MySQL and uploaded files&lt;/li&gt;
&lt;li&gt;Upgrades of the OS and components (security)&lt;/li&gt;
&lt;li&gt;Upgrades (security) of Drupal and modules&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Hint: DigitalOcean offer full disk image backups as a paid service so if I ever stop being cheap this resolves pretty quickly&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The full details are in a separate post but I'm pretty happy that setting up the box from scratch again, upgrading the various subcomponents, or even migrating to a different vendor will be a lot easier in the future (and won't affect any of the other projects I have going on).&lt;/p&gt;
&lt;h2 id="why-not-just-automate-the-monolith"&gt;Why not just automate the monolith&lt;/h2&gt;
&lt;p&gt;The "easy" answer may have been to automate more of my "ball of mud" to address the effort/efficiency of applying security updates.&lt;/p&gt;
&lt;p&gt;Yet the "better monolith" would mean I still owned the maintenance and uptime for a large percent of my services.&lt;/p&gt;
&lt;p&gt;The microservices approach of diversity means that with different providers (GitHub, Bitbucket, Google AppEngine, DigitalOcean, etc.) it is nearly impossible for them to all go down simultaneously or be affected by one another.&lt;/p&gt;
&lt;p&gt;Leveraging other platforms that better fit my use case means I benefit from their expertise and by reducing the moving parts I have a reduced security risk.&lt;/p&gt;
&lt;p&gt;My experiments in other frameworks and programming languages were never a good match for my "production" web services.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"because it's there" or "because I can" are very often the reason things continue to be done in a suboptimal way ;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now I will focus more on the "top" of the tech stack and high value content (like this blog post), less on the "how to automate and deploy" portion.&lt;/p&gt;
&lt;h2 id="ongoing-and-future-work"&gt;Ongoing and Future Work&lt;/h2&gt;
&lt;p&gt;I still have to purchase/renew domains, update DNS, and write content.&lt;/p&gt;
&lt;p&gt;I still have to eventually find a platform that allows my father-in-law to publish content (and upload files) where I'm not responsible for security patches or backups ;)&lt;/p&gt;
&lt;p&gt;My experimenting is now done via a PaaS like Google AppEngine, Heroku, Openshift etc. or using Docker containers.  That means more admin and cognitive sprawl but PaaS and CaaS are more predisposed to version control and elastic/disposable architecture so in all a lot less maintenance.&lt;/p&gt;
&lt;p&gt;The biggest new cost is managing the increased number of services but this at least makes explicit what I am working on and is mostly mitigated by automation.&lt;/p&gt;</content><category term="build-CI-CD-devops"/><category term="microservices"/><category term="monolith"/><category term="linode"/><category term="digitalocean"/><category term="packer"/><category term="drupal"/><category term="php"/><category term="python"/><category term="go"/><category term="cherokee"/><category term="nginx"/></entry><entry><title>Drupal with Docker Compose and nginx and php-fpm and mariadb</title><link href="https://blog.john-pfeiffer.com/drupal-with-docker-compose-and-nginx-and-php-fpm-and-mariadb/" rel="alternate"/><published>2016-07-28T23:59:00-07:00</published><updated>2016-07-28T23:59:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2016-07-28:/drupal-with-docker-compose-and-nginx-and-php-fpm-and-mariadb/</id><summary type="html">
&lt;h2 id="replacing-cherokee-webserver-php-cgi-mysql"&gt;Replacing: cherokee webserver, php-cgi, MySQL&lt;/h2&gt;
&lt;p&gt;In my professional life I've seen the webserver front end default choice shift from Apache to NGINX occur for production services.&lt;/p&gt;
&lt;p&gt;Nginx is compelling due to simpler configuration and improved performance so I suspect the existing preponderance of Apache deployments are because "if it ain't …&lt;/p&gt;</summary><content type="html">
&lt;h2 id="replacing-cherokee-webserver-php-cgi-mysql"&gt;Replacing: cherokee webserver, php-cgi, MySQL&lt;/h2&gt;
&lt;p&gt;In my professional life I've seen the webserver front end default choice shift from Apache to NGINX occur for production services.&lt;/p&gt;
&lt;p&gt;Nginx is compelling due to simpler configuration and improved performance so I suspect the existing preponderance of Apache deployments are because "if it ain't broke don't fix it" (which makes lots of sense in Operations) along with the large number of Ops/SysAdmins who already know how to configure and integrate Apache (which makes a lot of sense to Management).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.nginx.com/blog/nginx-vs-apache-our-view/"&gt;https://www.nginx.com/blog/nginx-vs-apache-our-view/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For a personal project I experimented with and chose Cherokee web server (&lt;a href="http://cherokee-project.com/"&gt;http://cherokee-project.com/&lt;/a&gt;) because it offered good performance and a simpler setup (Web UI even!)&lt;/p&gt;
&lt;p&gt;Unfortunately as the project Dev and adoption slowed it seemed to make a lot of sense to finally convert my personal project to nginx. &lt;a href="https://github.com/cherokee/webserver/commits/master"&gt;https://github.com/cherokee/webserver/commits/master&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I also wanted to experiment with a Docker based infrastructure (with docker-compose and a single YAML config file, ideally leveraging as much as possible the many official upstream docker images (&lt;a href="https://hub.docker.com/explore/"&gt;https://hub.docker.com/explore/&lt;/a&gt;))&lt;/p&gt;
&lt;p&gt;Benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;this would isolate my app from the Host OS (increased portability)&lt;/li&gt;
&lt;li&gt;allow for simpler component swapping or upgrades (with testing and rollback and even local Dev)&lt;/li&gt;
&lt;li&gt;leave room for adding other isolated components&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="almost-able-to-leverage-everything-right-out-of-the-box"&gt;Almost able to leverage everything right out of the box&lt;/h2&gt;
&lt;p&gt;I spent time researching nginx and php-fpm and created a minimal config that worked (two docker commands) based on the slimmer alpine linux docker images. &lt;a href="https://blog.john-pfeiffer.com/nginx-with-docker/"&gt;https://blog.john-pfeiffer.com/nginx-with-docker/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This wasn't quite far off from the official Drupal Dockerfile except there are some more OS dependencies like GD and XML so I instead opted to use the official Drupal Docker image. (Non alpine linux so larger size but they've done the work of packaging the complexity and by not customizing it will be easier to update in the future - and avoid a Docker Build entirely!)&lt;/p&gt;
&lt;h3 id="docker-composeyml-for-nginx-and-drupal7-fpm-and-mariadb55"&gt;docker-compose.yml for nginx and drupal:7-fpm and mariadb:5.5&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# https://docs.docker.com/compose/compose-file/&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.docker.com/compose/environment-variables/&lt;/span&gt;
&lt;span class="c1"&gt;# https://www.nginx.com/resources/wiki/start/topics/recipes/drupal&lt;/span&gt;

&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;alpine&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"80:80"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fpm&lt;/span&gt;

&lt;span class="c1"&gt;# https://hub.docker.com/_/drupal/&lt;/span&gt;
&lt;span class="n"&gt;fpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;drupal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fpm&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9000:9000"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;

&lt;span class="c1"&gt;# https://hub.docker.com/_/mariadb/&lt;/span&gt;
&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mariadb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;5.5&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3306:3306"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MYSQL_USER&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MYSQL_PASSWORD&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MYSQL_DATABASE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Adding the MariaDB (compatible with MySQL) image/container was fairly straightforward (just reading the docs on how to override and setup the default user and DB via Environment Variables)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I am ok with the Database files being kept completely inside of a persistent container.  I expect to do regular backups (mysqldump and scp) and may also spend the money to just use DigitalOcean's backup service (no poweroff required!)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="and-the-bugs"&gt;And the bugs&lt;/h3&gt;
&lt;p&gt;I discovered a bug where the css/themes didn't render correctly.  As part of the troubleshooting I installed nginx, php, and MySQL locally on the host with my override configs (no bug exhibited).  I then replaced each "native host installed service" with the Docker one, working from the backend DB forward I was able to identify the issue.&lt;/p&gt;
&lt;p&gt;Unfortunately the nginx image uses a default user of "nginx" whereas the drupal:php image uses "www-data" which caused issues when accessing the Drupal source files (which I was sharing via --volume on the host, preferring to eschew a "data container").&lt;/p&gt;
&lt;p&gt;My personal Linux maxim still holds true: "If there's a problem with something running in Linux it's probably a Permissions issue". ;)&lt;/p&gt;
&lt;h3 id="do-not-add-complexity"&gt;Do not add complexity&lt;/h3&gt;
&lt;p&gt;I avoided creating my own docker image build FROM drupal:php with nginx included because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;simplest with as few build steps possible&lt;/li&gt;
&lt;li&gt;contrary to the "do one thing well" principle&lt;/li&gt;
&lt;li&gt;contrary to the "one container one app" Docker principle&lt;/li&gt;
&lt;li&gt;tight coupling of nginx and php would make it harder to update one or the other independently&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cache-your-upstream-dependencies"&gt;Cache your upstream dependencies&lt;/h3&gt;
&lt;p&gt;Ironically I also ended up providing my own drupal.tar.gz downloaded from Drupal.org but cached in Bitbucket as the upstream ftp.drupal.org proved unreliable.&lt;/p&gt;
&lt;h2 id="the-pragmatic-solution"&gt;The pragmatic solution&lt;/h2&gt;
&lt;p&gt;Instead, as I was already using packer on DigitalOcean to automate building the Host and uploading the custom configs, docker-compose.yaml, and Drupal source, I added a few steps to install nginx directly to the Host OS.&lt;/p&gt;
&lt;h3 id="packerjson"&gt;packer.json&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;NEWUSER_NAME=username NEWUSER_PASSWORD=yourpassword DIGITALOCEAN_API_TOKEN=yourapitoken /opt/packer build packer.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;A few files and a single command (packer is just a single binary downloaded from hashicorp) I can have another Docker based Drupal host ready (awyeah)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"_comment"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.packer.io/docs/builders/digitalocean.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"variables"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"digitalocean_api_token"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{env `DIGITALOCEAN_API_TOKEN`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"newuser_name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{env `NEWUSER_NAME`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"newuser_password"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{env `NEWUSER_PASSWORD`}}"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"builders"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"digitalocean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"api_token"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{user `digitalocean_api_token`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"size"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"512mb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"region"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lon1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ubuntu-16-04-x64"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"droplet_name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"drupal-from-packer-{{timestamp}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"snapshot_name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"drupal-from-packer-{{isotime &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;2006.01.02.030405&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}}"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;}]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"provisioners"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"inline"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"ip a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"curl -s http://checkip.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"apt-get update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"sudo apt-get install -y vim curl wget byobu ntp tar gzip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"timedatectl set-timezone Etc/UTC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"cat /etc/timezone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"useradd -s /bin/bash -m {{user `newuser_name`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"usermod -a -G admin {{user `newuser_name`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"echo '{{user `newuser_name`}}:{{user `newuser_password`}}'|chpasswd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"cat /etc/passwd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"sed -i 's/Port 22/Port 2222/g' /etc/ssh/sshd_config"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"sed -i 's/PermitRootLogin yes/PermitRootLogin no/g' /etc/ssh/sshd_config"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"echo 'PasswordAuthentication no' &amp;gt;&amp;gt; /etc/ssh/sshd_config"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"cat /etc/ssh/sshd_config"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"mkdir -p /home/{{user `newuser_name`}}/.ssh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"fallocate -l 1G /swapfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"chmod 600 /swapfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"mkswap /swapfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"swapon /swapfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"swapon --show"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"free -h"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"echo 'vm.vfs_cache_pressure=50' | sudo tee -a /etc/sysctl.conf"&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"authorized_keys"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"destination"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/{{user `newuser_name`}}/.ssh/authorized_keys"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"script"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"install-docker.sh"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker-compose.yml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"destination"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/{{user `newuser_name`}}/docker-compose.yml"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"inline"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"mkdir -p /var/www/html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"wget https://bitbucket.org/yourusername/yourrepository/raw/97360be2edd53b93149d750db24f749aebc27988/binaries/drupal-7.50.tar.gz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"tar xf drupal-7.50.tar.gz --strip-components=1 -C /var/www/html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"ls -ahl /var/www/html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"mkdir -p /var/www/html/sites/default/files"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"chmod 777 /var/www/html/sites/default/files"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"cp -a /var/www/html/sites/default/default.settings.php /var/www/html/sites/default/settings.php"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"chmod 777 /var/www/html/sites/default/settings.php"&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"inline"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"apt-get install -y nginx"&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nginx.conf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"destination"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/etc/nginx/nginx.conf"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default.conf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"destination"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/etc/nginx/conf.d/default.conf"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;

&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;HINT: A DigitalOcean 512MB droplet needs swap enabled due to the MySQL/MariaDB memory defaults, otherwise you get strange errors about inode unavailable while starting the MariaDB container&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.suse.com/documentation/opensuse114/book_tuning/data/cha_tuning_memory_vm.html"&gt;https://www.suse.com/documentation/opensuse114/book_tuning/data/cha_tuning_memory_vm.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.7/2.6.7-mm1/broken-out/vfs-shrinkage-tuning.patch"&gt;https://www.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.7/2.6.7-mm1/broken-out/vfs-shrinkage-tuning.patch&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;One thing that is annoying is as a single partition it is potentially vulnerable to a /var/log DenialOfService&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;A very nice tool to add is fail2ban (apt-get install fail2ban) but I want to ensure I tweak the configs correctly&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="install-dockersh"&gt;install-docker.sh&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c1"&gt;# ubuntu 16.04 is xenial , https://blog.john-pfeiffer.com/docker-intro-install-run-and-port-forward/&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;apt-key&lt;span class="w"&gt; &lt;/span&gt;adv&lt;span class="w"&gt; &lt;/span&gt;--keyserver&lt;span class="w"&gt; &lt;/span&gt;hkp://pgp.mit.edu:80&lt;span class="w"&gt; &lt;/span&gt;--recv-keys&lt;span class="w"&gt; &lt;/span&gt;58118E89F3A912897C070ADBF76221572C52609D
sudo&lt;span class="w"&gt; &lt;/span&gt;sh&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo 'deb https://apt.dockerproject.org/repo ubuntu-xenial main' &amp;gt; /etc/apt/sources.list.d/docker.list"&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;update&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;linux-image-extra-&lt;span class="k"&gt;$(&lt;/span&gt;uname&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;docker-engine&lt;span class="w"&gt; &lt;/span&gt;docker-compose&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
service&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
docker&lt;span class="w"&gt; &lt;/span&gt;info&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;linux-image-extra is to ensure we have AUFS because docker needs a proper storage driver&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An alternative is to download the docker-compose binary directly (to /usr/local/bin):
- &lt;a href="https://github.com/docker/compose/releases"&gt;https://github.com/docker/compose/releases&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo curl -L "https://github.com/docker/compose/releases/download/1.25.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose&lt;/code&gt;
&lt;code&gt;sudo chmod +x /usr/local/bin/docker-compose&lt;/code&gt;
&lt;code&gt;which docker-compose ; sudo docker-compose --version&lt;/code&gt;&lt;/p&gt;
&lt;h4 id="docker-composeyml-with-drupal7-fpm-and-mariadb55"&gt;docker-compose.yml with drupal:7-fpm and mariadb:5.5&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;# https://hub.docker.com/_/drupal/&lt;/span&gt;
fpm:
&lt;span class="w"&gt;  &lt;/span&gt;image:&lt;span class="w"&gt; &lt;/span&gt;drupal:7-fpm
&lt;span class="w"&gt;  &lt;/span&gt;ports:
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1:9000:9000"&lt;/span&gt;
&lt;span class="c"&gt;#    - "9000:9000"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;volumes:
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;/var/www/html:/var/www/html/
&lt;span class="w"&gt;  &lt;/span&gt;links:
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;mysql

&lt;span class="c"&gt;# https://hub.docker.com/_/mariadb/&lt;/span&gt;
mysql:
&lt;span class="w"&gt;  &lt;/span&gt;image:&lt;span class="w"&gt; &lt;/span&gt;mariadb:5.5
&lt;span class="w"&gt;  &lt;/span&gt;ports:
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3306:3306"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;environment:
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;MYSQL_ROOT_PASSWORD
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;MYSQL_USER
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;MYSQL_PASSWORD
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;MYSQL_DATABASE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;2 out of 3 ain't bad&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="nginxconf"&gt;nginx.conf&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# slightly modified /etc/nginx/nginx.conf from "apt-get install nginx"&lt;/span&gt;
&lt;span class="c1"&gt;# https://www.nginx.com/resources/wiki/start/topics/examples/full/&lt;/span&gt;

&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;www-data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;worker_processes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/run/nginx.pid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;worker_connections&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# multi_accept on;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;sendfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;tcp_nopush&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;tcp_nodelay&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;keepalive_timeout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;types_hash_max_size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;server_tokens&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/etc/nginx/mime.types&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;default_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;application/octet-stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;access_log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/var/log/nginx/access.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;error_log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/var/log/nginx/error.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;gzip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;gzip_disable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"msie6"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;gzip_types&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;text/plain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;text/css&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;application/javascript&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;text/xml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;application/xml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;application/xml+rss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;text/javascript&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/etc/nginx/conf.d/*.conf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="defaultconf"&gt;default.conf&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;listen&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;server_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;physicstime.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/var/www/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;## &amp;lt;-- Your only path reference.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/favicon.ico&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;log_not_found&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;access_log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/robots.txt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;allow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;log_not_found&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;access_log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Very rarely should these ever be accessed outside of your lan&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;\.(txt|log)&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;deny&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sr"&gt;\..*/.*\.php$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sr"&gt;^/sites/.*/private/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Allow "Well-Known URIs" as per RFC 5785&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;^/.well-known/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;allow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Block access to "hidden" files and directories whose names begin with a&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# period. This includes directories used by version control systems such&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# as Subversion or Git to store control files.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sr"&gt;(^|/)\.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# try_files $uri @rewrite; # For Drupal &amp;lt;= 6&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;try_files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/index.php?&lt;/span&gt;&lt;span class="nv"&gt;$query_string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# For Drupal &amp;gt;= 7&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;@rewrite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;rewrite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;^/(.*)&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/index.php?q=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Don't allow direct access to PHP files in the vendor directory.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sr"&gt;/vendor/.*\.php$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;deny&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# In Drupal 8, we must also match new paths where the '.php' appears in&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# the middle, such as update.php/selection. The rule we use is strict,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# and only allows this pattern with the update.php front controller.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# This allows legacy path aliases in the form of&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# blog/index.php/legacy-path to continue to route to Drupal nodes. If&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# you do not have any paths like that, then you might prefer to use a&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# laxer rule, such as:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;#   location ~ \.php(/|$) {&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# The laxer rule will continue to work if Drupal uses this new URL&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# pattern with front controllers other than update.php in a future&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# release.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sr"&gt;'\.php$|^/update.php'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;fastcgi_split_path_info&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;^(.+?\.php)(|/.*)&lt;/span&gt;$&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# Security note: If you're running a version of PHP older than the&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# latest 5.3, you should have "cgi.fix_pathinfo = 0;" in php.ini.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# See http://serverfault.com/q/627903/94922 for details.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;fastcgi_params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# Block httpoxy attacks. See https://httpoxy.org/.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;fastcgi_param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;HTTP_PROXY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;fastcgi_param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;SCRIPT_FILENAME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$document_root$fastcgi_script_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;fastcgi_param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;PATH_INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$fastcgi_path_info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;fastcgi_intercept_errors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# PHP 5 socket location.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;#fastcgi_pass unix:/var/run/php5-fpm.sock;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# PHP 7 socket location.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;fastcgi_pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;127.0.0.1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# fastcgi_pass fpm:9000;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Fighting with Styles? This little gem is amazing.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# location ~ ^/sites/.*/files/imagecache/ { # For Drupal &amp;lt;= 6&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sr"&gt;^/sites/.*/files/styles/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# For Drupal &amp;gt;= 7&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;try_files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;@rewrite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Handle private files through Drupal.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sr"&gt;^/system/files/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# For Drupal &amp;gt;= 7&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;try_files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/index.php?&lt;/span&gt;&lt;span class="nv"&gt;$query_string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# prevent hotlinking&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sr"&gt;^/sites/.*/files/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;valid_referers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;blocked&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;www.physicstime.com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;physicstime.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$invalid_referer&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="kn"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;\.(js|css|png|jpg|jpeg|gif|ico)&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# prevent hotlinking&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;valid_referers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;blocked&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;www.physicstime.com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;physicstime.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$invalid_referer&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="kn"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;expires&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;max&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;log_not_found&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;slightly modified from the wonderful reference provided by nginx, mostly the first 3 lines and later the fastcgi_pass&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nginx.com/resources/wiki/start/topics/recipes/drupal/"&gt;https://www.nginx.com/resources/wiki/start/topics/recipes/drupal/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="post-boot-manual-configuration-ohgodwhy"&gt;Post boot manual configuration (ohgodwhy)&lt;/h2&gt;
&lt;p&gt;Post boot of a "fresh from built-by-packer snapshot" there were still the basic steps of configuring Drupal (which I did manually with a browser and install.php though I'm pretty sure I could have just overridden the settings.php directly).&lt;/p&gt;
&lt;p&gt;And for this migration project a MySQL dump, SCP of the existing extra modules, and of course the already uploaded user images/files.&lt;/p&gt;
&lt;p&gt;I'm sure with more tinkering I can overcome the nginx vs www-data user permissions issue but since I time boxed this project I stuck with this compromise which is still much more improved and automated than my previous setup.&lt;/p&gt;
&lt;p&gt;For migrating data there is of course the prerequisite: &lt;code&gt;mysqldump -uroot -p physicstime &amp;gt; backup.sql&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;vi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bartik&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.929&lt;/span&gt;&lt;span class="n"&gt;em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bartik&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/#site-slogan {&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s1"&gt;  font-size: 0.929em/#site-slogan {&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s1"&gt;  font-size: 2.929em/'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bartik&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;customize increased visibility of the #site-slogan { , occasionally needs cache cleared in admin/config/development/performance
The oneliner is a complicated way of sed replacing two lines at once (tr replaces newlines with \r temporarily)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/font-size: 87.5/font-size: 120/'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bartik&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Because the world does not need yet another 10pt font website&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;hostnamectl set-hostname physicstime.com
docker-compose up &amp;amp;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Hint: &lt;code&gt;/etc/hosts&lt;/code&gt; locally and use a browser to do initial install.php config before a DNS cutover&lt;/p&gt;
&lt;p&gt;DB connection string requires 172.17.0.1 (the default docker private IP network bridge)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;physicstime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;physicstime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="c1"&gt;# mv files/ /var/www/html/sites/physicstime.com/&lt;/span&gt;
&lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sites&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="n"&gt;ifconfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bc&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;172.17&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;

&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mariadb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;5.5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bash&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;h172&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;17.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;uYOURUSER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;physicstime&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;h172&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;17.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;uroot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;physicstime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;

&lt;span class="n"&gt;chown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sites&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="n"&gt;chmod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sites&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;ensure /var/www/html/sites/default/files has the correct permissions, otherwise no CSS for you!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;compose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;
&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;compose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;
&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;compose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tee&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;physicstime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;physicstime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This starts it manually and displays any traffic&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;touch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;physicstime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chmod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;physicstime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;physicstime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"starting physicstime after boot"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;physicstime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;YOURUSER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;compose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;

&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;physicstime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;this starts the docker based services automatically on boot (not including extra logging)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2 id="adhoc-performance-and-latency-testing"&gt;Adhoc Performance and Latency Testing&lt;/h2&gt;
&lt;p&gt;Data driven decisions is a big deal, but let's be honest, engineers love numbers :)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ab -l -r -n 500 -c 100 -k -H "Accept-Encoding: gzip, deflate" http://physicstime.com/node?page=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The unscientific "watching top": 80% for about 5 seconds vs 78% for about 5 seconds&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://httpd.apache.org/docs/2.4/programs/ab.html"&gt;https://httpd.apache.org/docs/2.4/programs/ab.html&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="linode-and-cherokee-and-php-cgi-53-and-mysql-55"&gt;Linode and Cherokee and php-cgi 5.3 and MySQL 5.5&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Concurrency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Level&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;taken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;7.255&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;
&lt;span class="n"&gt;Complete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;
&lt;span class="n"&gt;Failed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;Keep&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Alive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;transferred&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;5321000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;
&lt;span class="n"&gt;HTML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;transferred&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mi"&gt;5089500&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;
&lt;span class="n"&gt;Requests&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;second&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;68.92&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;#/sec&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mf"&gt;1451.029&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mf"&gt;14.510&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;across&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;concurrent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Transfer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="mf"&gt;716.22&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Kbytes/sec&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;received&lt;/span&gt;

&lt;span class="k"&gt;Connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Times&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;+/-sd&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;median&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;
&lt;span class="k"&gt;Connect&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="nl"&gt;Processing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1313&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;320.0&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1437&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;1525&lt;/span&gt;
&lt;span class="nl"&gt;Waiting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mi"&gt;89&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1308&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;320.2&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1431&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;1521&lt;/span&gt;
&lt;span class="nl"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;104&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1315&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;319.8&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1439&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;1526&lt;/span&gt;

&lt;span class="n"&gt;Percentage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;served&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;within&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;certain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1439&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1456&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1466&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1473&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1488&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1505&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;98&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1516&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1519&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1526&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;longest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="digitalocean-and-nginx-and-docker-php-fpm-and-mariadb-55"&gt;DigitalOcean and nginx and Docker php-fpm and MariaDB 5.5&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Concurrency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Level&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;taken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;7.791&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;
&lt;span class="n"&gt;Complete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;
&lt;span class="n"&gt;Failed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;Keep&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Alive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;transferred&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;5320068&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;
&lt;span class="n"&gt;HTML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;transferred&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mi"&gt;5082000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;
&lt;span class="n"&gt;Requests&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;second&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;64.18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;#/sec&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mf"&gt;1558.145&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mf"&gt;15.581&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;across&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;concurrent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Transfer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="mf"&gt;666.87&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Kbytes/sec&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;received&lt;/span&gt;

&lt;span class="k"&gt;Connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Times&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;+/-sd&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;median&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;
&lt;span class="k"&gt;Connect&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="nl"&gt;Processing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;542&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1421&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;201.6&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1453&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;
&lt;span class="nl"&gt;Waiting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;531&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1417&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;201.2&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1447&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;2017&lt;/span&gt;
&lt;span class="nl"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;545&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1422&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;201.0&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1453&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;2022&lt;/span&gt;

&lt;span class="n"&gt;Percentage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;served&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;within&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;certain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1453&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1470&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1485&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1494&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1510&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1674&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;98&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1877&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1930&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;longest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;So it seems there's a slight edge in either cherokee over nginx OR not running dockerized applications OR linode vs digitalocean BUT...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When I tweaked it for higher concurrency &lt;code&gt;ab -l -r -n 500 -c 200 -k -H "Accept-Encoding: gzip, deflate" http://physicstime.com/node?page=8&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Linode + Cherokee + php-cgi&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;taken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;9.933&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;
&lt;span class="n"&gt;Requests&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;second&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;50.34&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;#/sec&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mf"&gt;3973.367&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;DigitalOcean + Nginx + Docker + php-fpm&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;taken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mf"&gt;8.120&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;
&lt;span class="n"&gt;Requests&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;second&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;61.58&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;#/sec&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mf"&gt;3248.067&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I can see the trend reverse (shrug)&lt;/p&gt;
&lt;h4 id="latency-testing"&gt;Latency testing&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://tools.pingdom.com/"&gt;https://tools.pingdom.com/&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;website speed test (NY) for cherokee and php-cgi had a load time of 1.17s&lt;/li&gt;
&lt;li&gt;website speed test (NY) for nginx and Docker had a load time of 1.46s&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;meh&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="conclusions"&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;Adding docker simplifies one kind of complexity (stringing together multiple services that are packaged upstream) but can come at some cost to performance. (Though this might also be due to the cheaper node price)&lt;/p&gt;
&lt;p&gt;The other major advantage of using docker is that different components/services can be upgraded independently (and even just "test upgraded") which allows for a faster adoption of upstream project improvements.&lt;/p&gt;
&lt;p&gt;I personally don't like relying on the OS of the host for all of the global dependencies to play nice (and especially that different packages won't have conflicting dependencies).&lt;/p&gt;
&lt;p&gt;Since all of these processes are running in the same host that I own I expect I'm not worse off security wise.&lt;/p&gt;
&lt;p&gt;I'm curious to see if given enough time I run into the infamous /var/lib/docker issues of orphan containers, running out of disk space, or other issues.&lt;/p&gt;
&lt;p&gt;A next step might be to run this in a PlatormAsAService like OpenShift or better yet break up each container to run via a ContainerAsAService (if it's free ;)&lt;/p&gt;
&lt;p&gt;Obviously if performance and scale of a mostly read-only content distribution system was really an issue adding a cache layer like Varnish or Cloudflare or even just tweaking the various configurations would help =]&lt;/p&gt;</content><category term="virtualization"/><category term="docker"/><category term="docker-compose"/><category term="digitalocean"/><category term="packer"/><category term="drupal"/><category term="php"/><category term="nginx"/></entry><entry><title>Alpine Linux Introduction Tutorial</title><link href="https://blog.john-pfeiffer.com/alpine-linux-introduction-tutorial/" rel="alternate"/><published>2016-02-28T21:19:00-08:00</published><updated>2016-02-28T21:19:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2016-02-28:/alpine-linux-introduction-tutorial/</id><summary type="html">
&lt;p&gt;Alpine Linux is a minimalist secure linux distro.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In security terms less "footprint" often means less vectors of attack and less complexity to analyze for vulnerabilities&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Alpine Linux is becoming a preferred base OS for many foundational official Docker Images (python, php, ruby, nginx, redis, haproxy, go) since downloading many …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Alpine Linux is a minimalist secure linux distro.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In security terms less "footprint" often means less vectors of attack and less complexity to analyze for vulnerabilities&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Alpine Linux is becoming a preferred base OS for many foundational official Docker Images (python, php, ruby, nginx, redis, haproxy, go) since downloading many large Docker Images (aka Deploying Docker Containers) can saturate the network at scale.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.pcworld.com/article/3031765/is-docker-ditching-ubuntu-linux-confusion-reigns.html"&gt;http://www.pcworld.com/article/3031765/is-docker-ditching-ubuntu-linux-confusion-reigns.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.ycombinator.com/item?id=10998667"&gt;https://news.ycombinator.com/item?id=10998667&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Alpine_Linux"&gt;https://en.wikipedia.org/wiki/Alpine_Linux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="getting-started-with-alpine-linux-in-docker"&gt;Getting started with Alpine Linux in Docker&lt;/h3&gt;
&lt;p&gt;This will pull the latest alpine image (around 4MB) and run it in an ephemeral container.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo docker run -it --rm alpine /bin/sh&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/alpine/"&gt;https://hub.docker.com/_/alpine/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="basics"&gt;Basics&lt;/h3&gt;
&lt;p&gt;Most of the very basic commands are similar to other linux distros like Debian/Ubuntu/Redhat, but of course there are differences ;)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -l /bin/sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;/bin/busybox&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat /etc/passwd
less /etc/passwd
vi /etc/passwd
grep root /etc/passwd
ls -l /usr/bin /usr/sbin | more
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;more busybox&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="package-management"&gt;Package Management&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apk update
apk --help
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;update the local index for all remote packages, list the options of the package manager&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apk info
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;list all of the packages installed locally&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apk search curl
apk search curl | sort
apk info curl
apk add curl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;search the remote packages for a keyword (unsorted results), get info for a specific package, install a specific package&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management"&gt;http://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pkgs.alpinelinux.org/packages"&gt;https://pkgs.alpinelinux.org/packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="networking"&gt;Networking&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;hostname
cat /etc/resolv.conf
ifconfig
netstat -anp

traceroute
apk add iptables
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="compiling-c-on-alpine-linux"&gt;Compiling C on Alpine Linux&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apk add build-base gcc abuild binutils
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;This should probably be part of a Dockerfile rather than run every time in an ephemeral container&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://wiki.alpinelinux.org/wiki/How_to_get_regular_stuff_working#Compiling_:_a_few_notes_and_a_reminder"&gt;https://wiki.alpinelinux.org/wiki/How_to_get_regular_stuff_working#Compiling_:_a_few_notes_and_a_reminder&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vi hi.c&lt;/code&gt;&lt;/p&gt;
&lt;h4 id="hic"&gt;hi.c&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hi"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="compiling"&gt;compiling&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apk add file
gcc -static hi.c
chmod +x a.out
file a.out
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The default is a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;./a.out
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;hi&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that this was just gcc not musl-gcc :(&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="incomplete-musl-compiler-on-alpine-linux"&gt;Incomplete musl compiler on alpine linux&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;WARNING: below did not work, TODO: &lt;a href="https://bitbucket.org/GregorR/musl-cross"&gt;https://bitbucket.org/GregorR/musl-cross&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I suspect that I am overwriting the existing gcc toolchain and I need to specify a different prefix (/usr/local) &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apk update
apk add wget tar gzip gcc make
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id="apk-add-musl-dev-wget-tar-gzip-gcc-make"&gt;apk add musl-dev wget tar gzip gcc make&lt;/h1&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;wget --no-check-certificate http://www.musl-libc.org/releases/musl-1.1.15.tar.gz
tar xf musl-1.1.15.tar.gz
cd musl-1.1.15
./configure
make install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Now we've installed the musl compiler?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;/usr/local/musl seemed terribly empty of binaries (i.e. no /usr/local/musl/bin/)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.musl-libc.org/faq.html"&gt;https://www.musl-libc.org/faq.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://www.musl-libc.org/how.html"&gt;http://www.musl-libc.org/how.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;apk add alpine-sdk&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But maybe because it's already an alpine linux container the gcc already uses musl instead of libc and does not need the musl-gcc wrapper?&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.guidesbyeric.com/statically-link-c-programs-with-musl-gcc"&gt;http://www.guidesbyeric.com/statically-link-c-programs-with-musl-gcc&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="compile-go-on-alpine-linux"&gt;Compile Go on Alpine Linux&lt;/h3&gt;
&lt;p&gt;Just use the golang image based on alpine linux ;)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/docker-library/docs/tree/master/golang"&gt;https://github.com/docker-library/docs/tree/master/golang&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker pull golang:alpine
docker run -it --rm golang:alpine /bin/sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Now that we've got the container running we can use the shell&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;vi intro.go&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fmt"&lt;/span&gt;

&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"hi"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;To just run the source code &lt;code&gt;go run intro.go&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;hi&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-l
go&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;intro.go
ls&lt;span class="w"&gt; &lt;/span&gt;-l
./intro
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;hi&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Maybe &lt;/p&gt;
&lt;p&gt;&lt;a href="http://dominik.honnef.co/posts/2015/06/go-musl/"&gt;http://dominik.honnef.co/posts/2015/06/go-musl/&lt;/a&gt; ?&lt;/p&gt;
&lt;h3 id="git-with-alpine"&gt;Git with Alpine&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apk&lt;span class="w"&gt; &lt;/span&gt;update
apk&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;--no-cache&lt;span class="w"&gt; &lt;/span&gt;git
git&lt;span class="w"&gt; &lt;/span&gt;--version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="linux"/><category term="alpine"/><category term="linux"/><category term="security"/></entry><entry><title>Debian Packages for Deployment and Reprepro for a local apt repository</title><link href="https://blog.john-pfeiffer.com/debian-packages-for-deployment-and-reprepro-for-a-local-apt-repository/" rel="alternate"/><published>2016-02-24T20:40:00-08:00</published><updated>2016-02-24T20:40:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2016-02-24:/debian-packages-for-deployment-and-reprepro-for-a-local-apt-repository/</id><summary type="html">
&lt;h3 id="what-is-a-debian-package"&gt;What is a Debian Package?&lt;/h3&gt;
&lt;p&gt;A debian package is a way to distribute and install a collection of files (aka software) onto a system (i.e. Debian or Ubuntu).&lt;/p&gt;
&lt;p&gt;While a piece of software might depend on other debian packages (e.g. libraries) usually a single .deb file represents some …&lt;/p&gt;</summary><content type="html">
&lt;h3 id="what-is-a-debian-package"&gt;What is a Debian Package?&lt;/h3&gt;
&lt;p&gt;A debian package is a way to distribute and install a collection of files (aka software) onto a system (i.e. Debian or Ubuntu).&lt;/p&gt;
&lt;p&gt;While a piece of software might depend on other debian packages (e.g. libraries) usually a single .deb file represents some sort of module that serves a single purpose.&lt;/p&gt;
&lt;p&gt;Once a debian package is built any client (dpkg or apt which also uses dpkg ;) can use it to install the software.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.debian.org/doc/manuals/debian-faq/ch-pkg_basics.en.html"&gt;https://www.debian.org/doc/manuals/debian-faq/ch-pkg_basics.en.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="why-use-a-debian-package"&gt;Why use a Debian Package?&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;When you're developing on your own box you can pretty much get away with anything&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A complex and large scale production environment typically has a lot of costs (both operationally and in not becoming a bottleneck to dev velocity). Any opportunity to increase determinism and reduce risk is welcome.&lt;/p&gt;
&lt;p&gt;Deploying source code directly from version control does not always scale well (streaming tons of small files, dedicated read only service user direct to production, etc.) nor does it create enough determinism with regards to dependency management.&lt;/p&gt;
&lt;p&gt;As deployments become more frequent and Continuous Integration becomes more complex it is really important to embrace the "build once" principle so that a single artifact (hopefully with all of its dependencies) can pass through the gauntlet of integration testing and canary/incremental rollout.&lt;/p&gt;
&lt;p&gt;So now that you're convinced "Artifacts" are the way to go lets just skip .exe, .msi, .jar, .etc and go straight to...&lt;/p&gt;
&lt;p&gt;The Debian Package is a "battle tested" format with lots of features (dependency requirements, preinst scripts, postinst scripts, etc.) but if there is a bug in a specific .deb file it is not always practical to get the full source code and rebuild the whole thing (especially considering static bindings and specific compilation environment/parameters).&lt;/p&gt;
&lt;p&gt;One example people give is an erroneous pre install or post install script that is preventing either installation or removal.&lt;/p&gt;
&lt;p&gt;The example below is more on just simply changing the control file "Description:"&lt;/p&gt;
&lt;h4 id="how-to-unpack-a-debian-package-modify-the-control-file-and-repack-it"&gt;How to unpack a debian package, modify the control file, and repack it&lt;/h4&gt;
&lt;p&gt;To unpack, modify, and repack a debian package:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;--it&lt;span class="w"&gt; &lt;/span&gt;--volume&lt;span class="w"&gt; &lt;/span&gt;/tmp:/tmp&lt;span class="w"&gt; &lt;/span&gt;ubuntu:14.04&lt;span class="w"&gt; &lt;/span&gt;/bin/bash
sudo&lt;span class="w"&gt; &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;update
apt-get&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--yes&lt;span class="w"&gt; &lt;/span&gt;vim&lt;span class="w"&gt; &lt;/span&gt;wget
wget&lt;span class="w"&gt; &lt;/span&gt;https://example.com/example.deb&lt;span class="w"&gt; &lt;/span&gt;--output-document&lt;span class="w"&gt; &lt;/span&gt;/tmp/example.deb
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp
mkdir&lt;span class="w"&gt; &lt;/span&gt;emptydir
dpkg-deb&lt;span class="w"&gt; &lt;/span&gt;-R&lt;span class="w"&gt; &lt;/span&gt;example.deb&lt;span class="w"&gt; &lt;/span&gt;/tmp/emptydir
ls&lt;span class="w"&gt; &lt;/span&gt;-ahl&lt;span class="w"&gt; &lt;/span&gt;/tmp/emptydir
ls&lt;span class="w"&gt; &lt;/span&gt;-ahl&lt;span class="w"&gt; &lt;/span&gt;/tmp/emptydir/DEBIAN
vi&lt;span class="w"&gt; &lt;/span&gt;/tmp/emptydir/DEBIAN/control
dpkg-deb&lt;span class="w"&gt; &lt;/span&gt;-b&lt;span class="w"&gt; &lt;/span&gt;emptydir&lt;span class="w"&gt; &lt;/span&gt;/tmp/example-fixed.deb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;On your host /tmp should now contain example.deb and example-fixed.deb&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://unix.stackexchange.com/questions/138188/easily-unpack-deb-edit-postinst-and-repack-deb"&gt;https://unix.stackexchange.com/questions/138188/easily-unpack-deb-edit-postinst-and-repack-deb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://manpages.ubuntu.com/manpages/xenial/en/man1/dpkg-deb.1.html"&gt;http://manpages.ubuntu.com/manpages/xenial/en/man1/dpkg-deb.1.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="reprepro-for-a-local-apt-repository"&gt;reprepro for a local apt repository&lt;/h3&gt;
&lt;p&gt;Just as a debian provides more control over packaging and dependency management, you can also have your apt repository where you store debian packages.&lt;/p&gt;
&lt;p&gt;By hosting your own apt repository you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create your own distribution server (e.g. in an s3 bucket)&lt;/li&gt;
&lt;li&gt;Create your own intermediate mirror or cache of an upstream repository (e.g. in a local area network shared drive)&lt;/li&gt;
&lt;li&gt;Create a local apt repository on local disk for a non internet connected device&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tool &lt;code&gt;reprepro&lt;/code&gt; creates and manages the apt database and filesystem.&lt;/p&gt;
&lt;h4 id="setup-ubuntu-1404-to-install-reprepro"&gt;Setup Ubuntu 14.04 to install reprepro&lt;/h4&gt;
&lt;p&gt;It might be as simple as a single command to install reprepro but here is the full example in the case where you have broken or corrupted your sources.list:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; EOF &amp;gt; /etc/apt/sources.list&lt;/span&gt;
&lt;span class="s"&gt;    deb http://archive.ubuntu.com/ubuntu/ trusty main&lt;/span&gt;
&lt;span class="s"&gt;    deb http://archive.ubuntu.com/ubuntu/ trusty universe&lt;/span&gt;
&lt;span class="s"&gt;    deb http://archive.ubuntu.com/ubuntu/ trusty multiverse&lt;/span&gt;
&lt;span class="s"&gt;    deb http://security.ubuntu.com/ubuntu trusty-security main restricted&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;

rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;/var/lib/apt/lists
apt-get&lt;span class="w"&gt; &lt;/span&gt;clean&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;update
apt-get&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;reprepro
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://manpages.ubuntu.com/manpages/trusty/en/man1/reprepro.1.html"&gt;http://manpages.ubuntu.com/manpages/trusty/en/man1/reprepro.1.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="setup-the-gpg-key"&gt;Setup the GPG key&lt;/h4&gt;
&lt;p&gt;A gpg key is an important part of apt for providing a digital signature of authenticity&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;gpg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;
&lt;span class="n"&gt;gpg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fingerprint&lt;/span&gt;
&lt;span class="n"&gt;gpg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fingerprint&lt;/span&gt;
&lt;span class="n"&gt;gpg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;allow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;YOURKEY.gpg&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This allows for importing an existing gpg key into the local keyring (otherwise reprepro actions will not persist)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;gpg --yes --batch --delete-secret-keys "21E29B5B3F6D550EF4E2C2C9E9991E312341234"
gpg --yes --batch --delete-keys "21E29B5B3F6D550EF4E2C2C9E9991E312341234"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Removing or deleting a key seems to only work when you delete a key exactly by fingerprint&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;echo ENCPASSWORD | gpg --yes --no-tty --batch --passphrase-fd 0 --output 8F13E123.key --decrypt 8F13E123.key.gpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Decrypt a password encrypted gpg key (that was encrypted with gpg - so meta!)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wiki.debian.org/SettingUpSignedAptRepositoryWithReprepro"&gt;https://wiki.debian.org/SettingUpSignedAptRepositoryWithReprepro&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="setup-a-simple-reprepro"&gt;Setup a simple reprepro&lt;/h4&gt;
&lt;p&gt;Here we will setup a local apt mirror that is a filtered subset of the upstream mariadb repository&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/home/admin/apt/conf
mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/home/admin/apt/logs
mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/home/admin/apt/archive

cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; EOF &amp;gt; /home/admin/apt/conf/distributions&lt;/span&gt;
&lt;span class="s"&gt;Origin: digitalocean-mariadb-10-trusty&lt;/span&gt;
&lt;span class="s"&gt;Codename: digitalocean-mariadb-10-trusty&lt;/span&gt;
&lt;span class="s"&gt;Description: local mirror of mariadb 10 trusty from the digital ocean sf mirror&lt;/span&gt;
&lt;span class="s"&gt;Architectures: amd64&lt;/span&gt;
&lt;span class="s"&gt;Components: main&lt;/span&gt;
&lt;span class="s"&gt;SignWith: 8F13E123&lt;/span&gt;
&lt;span class="s"&gt;Update: - digitalocean-mariadb-10-trusty&lt;/span&gt;
&lt;span class="s"&gt;Log: /home/admin/apt/logs/bintray-mirror.log&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;

cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; EOF &amp;gt; /home/admin/apt/conf/options&lt;/span&gt;
&lt;span class="s"&gt;    outdir /home/admin/apt/archive&lt;/span&gt;
&lt;span class="s"&gt;    ask-passphrase&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;

cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; EOF &amp;gt; /home/admin/apt/conf/updates&lt;/span&gt;
&lt;span class="s"&gt;Name: digitalocean-mariadb-10-trusty&lt;/span&gt;
&lt;span class="s"&gt;Suite: trusty&lt;/span&gt;
&lt;span class="s"&gt;Method: http://sfo1.mirrors.digitalocean.com/mariadb/repo/10.0/ubuntu/&lt;/span&gt;
&lt;span class="s"&gt;Components: main&lt;/span&gt;
&lt;span class="s"&gt;Architectures: amd64&lt;/span&gt;
&lt;span class="s"&gt;FilterList: deinstall /home/admin/apt/conf/mariadb-partial.list&lt;/span&gt;
&lt;span class="s"&gt;VerifyRelease: blindtrust&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;

cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; EOF &amp;gt; /home/admin/apt/conf/mariadb-partial.list&lt;/span&gt;
&lt;span class="s"&gt;mariadb-partial.list&lt;/span&gt;
&lt;span class="s"&gt;libmariadbclient-dev install&lt;/span&gt;
&lt;span class="s"&gt;libmariadbclient18 install&lt;/span&gt;
&lt;span class="s"&gt;libmariadbd-dev install&lt;/span&gt;
&lt;span class="s"&gt;libmysqlclient18 install&lt;/span&gt;
&lt;span class="s"&gt;mariadb-client install&lt;/span&gt;
&lt;span class="s"&gt;mariadb-client-10.0 install&lt;/span&gt;
&lt;span class="s"&gt;mariadb-client-core-10.0 install&lt;/span&gt;
&lt;span class="s"&gt;mariadb-common install&lt;/span&gt;
&lt;span class="s"&gt;mariadb-connect-engine-10.0 install&lt;/span&gt;
&lt;span class="s"&gt;mariadb-server install&lt;/span&gt;
&lt;span class="s"&gt;mariadb-server-10.0 install&lt;/span&gt;
&lt;span class="s"&gt;mariadb-server-core-10.0 install&lt;/span&gt;
&lt;span class="s"&gt;mysql-common install&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The configuration created
- defines the Distribution
- the option of output directory
- how the Distribution becomes updated (blindtrust (lol))
- explicitly what to download or blacklist from the upstream (exclude everything else)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mirrorer.alioth.debian.org/reprepro.1.html#CONFIG FILES"&gt;https://mirrorer.alioth.debian.org/reprepro.1.html#CONFIG FILES&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="updating-a-remote-source"&gt;Updating a remote source&lt;/h4&gt;
&lt;p&gt;Assuming the installation of reprepro and correct configuration of conf/options, conf/distributions, conf/updates, and conf/NAME-partial.list&lt;/p&gt;
&lt;p&gt;Update a local repository from an upstream:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;reprepro&lt;span class="w"&gt; &lt;/span&gt;--verbose&lt;span class="w"&gt; &lt;/span&gt;--basedir&lt;span class="w"&gt; &lt;/span&gt;/home/admin/apt&lt;span class="w"&gt; &lt;/span&gt;check&lt;span class="w"&gt; &lt;/span&gt;digitalocean-mariadb-10-trusty

reprepro&lt;span class="w"&gt; &lt;/span&gt;--verbose&lt;span class="w"&gt; &lt;/span&gt;--basedir&lt;span class="w"&gt; &lt;/span&gt;/home/admin/apt&lt;span class="w"&gt; &lt;/span&gt;update&lt;span class="w"&gt; &lt;/span&gt;digitalocean-mariadb-10-trusty
&lt;span class="w"&gt;    &lt;/span&gt;aptmethod&lt;span class="w"&gt; &lt;/span&gt;got&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'http://sfo1.mirrors.digitalocean.com/mariadb/repo/10.0/ubuntu/dists/trusty/InRelease'&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;aptmethod&lt;span class="w"&gt; &lt;/span&gt;got&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'http://sfo1.mirrors.digitalocean.com/mariadb/repo/10.0/ubuntu/dists/trusty/main/binary-amd64/Packages.gz'&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;Calculating&lt;span class="w"&gt; &lt;/span&gt;packages&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;get...
&lt;span class="w"&gt;    &lt;/span&gt;Getting&lt;span class="w"&gt; &lt;/span&gt;packages...
&lt;span class="w"&gt;    &lt;/span&gt;aptmethod&lt;span class="w"&gt; &lt;/span&gt;got&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'http://sfo1.mirrors.digitalocean.com/mariadb/repo/10.0/ubuntu/pool/main/m/mariadb-10.0/libmariadbclient-dev_10.0.26+maria-1~trusty_amd64.deb'&lt;/span&gt;

ls&lt;span class="w"&gt; &lt;/span&gt;-ahl&lt;span class="w"&gt; &lt;/span&gt;/home/admin/apt/archive
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;should contain two directories: dists and pool&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;reprepro --verbose --basedir /home/admin/apt check
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This will check all Distributions that have been configured for upstream changes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="listing-or-adding-or-removing-a-debian-package-with-reprepro"&gt;Listing or Adding or Removing a debian package with reprepro&lt;/h4&gt;
&lt;p&gt;Assuming you have setup your gpg signing key and config files correctly you can also just add a single package ad-hoc to your local apt repository&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;reprepro&lt;span class="w"&gt; &lt;/span&gt;dumpreferences
reprepro&lt;span class="w"&gt; &lt;/span&gt;--verbose&lt;span class="w"&gt; &lt;/span&gt;--basedir&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;remove&lt;span class="w"&gt; &lt;/span&gt;digitalocean-mariadb-10-trusty&lt;span class="w"&gt; &lt;/span&gt;SOMEPACKAGENAME
reprepro&lt;span class="w"&gt; &lt;/span&gt;--verbose&lt;span class="w"&gt; &lt;/span&gt;--basedir&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;includedeb&lt;span class="w"&gt; &lt;/span&gt;digitalocean-mariadb-10-trusty&lt;span class="w"&gt; &lt;/span&gt;SOMEFILENAME.deb
reprepro&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;digitalocean-mariadb-10-trusty
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mirrorer.alioth.debian.org/reprepro.1.html"&gt;https://mirrorer.alioth.debian.org/reprepro.1.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wikitech.wikimedia.org/wiki/Reprepro"&gt;https://wikitech.wikimedia.org/wiki/Reprepro&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="configuring-a-client"&gt;Configuring a client&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/etc/apt/sources.list.d/mariadb.list&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;deb&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="err"&gt;file:///home/admin/apt/archive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;trusty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;main&lt;/span&gt;
&lt;span class="err"&gt;apt-get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;clean&lt;/span&gt;
&lt;span class="err"&gt;apt-get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;update&lt;/span&gt;
&lt;span class="err"&gt;apt-get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;mariadb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Now you can install mariadb from your local apt repository, or host it on s3 and change mariadb.list to have the bucket FQDN (&lt;code&gt;aws s3 --delete --exact-timestamps sync ./archive s3://mybucket&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;</content><category term="build-CI-CD-devops"/><category term="debian"/><category term="deb"/><category term="build"/><category term="packaging"/><category term="deployment"/><category term="scale"/><category term="reprepro"/><category term="apt"/></entry><entry><title>Python packaging pip wheels</title><link href="https://blog.john-pfeiffer.com/python-packaging-pip-wheels/" rel="alternate"/><published>2016-01-05T20:00:00-08:00</published><updated>2016-01-05T20:00:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2016-01-05:/python-packaging-pip-wheels/</id><summary type="html">
&lt;h3 id="installing-packages-with-python"&gt;Installing packages with python&lt;/h3&gt;
&lt;p&gt;Pip is the standard way to install python packages&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packagename&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;searches &lt;a href="https://pypi.python.org"&gt;https://pypi.python.org&lt;/a&gt; and finds the latest version of the package, full docs &lt;a href="https://pip.pypa.io/en/latest/"&gt;https://pip.pypa.io/en/latest/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you get the package name wrong you will have installed something …&lt;/p&gt;</summary><content type="html">
&lt;h3 id="installing-packages-with-python"&gt;Installing packages with python&lt;/h3&gt;
&lt;p&gt;Pip is the standard way to install python packages&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packagename&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;searches &lt;a href="https://pypi.python.org"&gt;https://pypi.python.org&lt;/a&gt; and finds the latest version of the package, full docs &lt;a href="https://pip.pypa.io/en/latest/"&gt;https://pip.pypa.io/en/latest/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you get the package name wrong you will have installed something completely different.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packagename&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find packages similar to the name you provided (from pypi or whatever provider you are using)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://pip.pypa.io/en/stable/reference/pip_search/"&gt;https://pip.pypa.io/en/stable/reference/pip_search/&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo pip freeze
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;displays what is installed&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="virtualenv-for-sanity-and-isolation"&gt;Virtualenv for sanity and isolation&lt;/h3&gt;
&lt;p&gt;A common mistake is to use the global pip installation of the Operating System to store all of the installed dependencies.  As soon as you have conflicting version requirements this breaks.  As soon as you have multiple applications/services installing globally it becomes unmanageable.&lt;/p&gt;
&lt;p&gt;Virtualenv creates a virtual environment (basically injecting a PATH into the environment) for python binaries and package installation.&lt;/p&gt;
&lt;h4 id="pinning-versions-and-guaranteed-sourcing"&gt;Pinning Versions and Guaranteed Sourcing&lt;/h4&gt;
&lt;p&gt;One common mistake is to not pin version numbers and depend on &lt;a href="https://pypi.python.org"&gt;https://pypi.python.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Without pinning version numbers for your dependencies (listed one per line in requirements.txt) you will receive a nasty surprise when the maintainers make a breaking change and you get a newer version unexpectedly.  Since python is a dynamic language you may receive the worst kind of surprise in production (hopefully nothing as bad as data corruption or security issues).&lt;/p&gt;
&lt;p&gt;When you do pin the version number BUT still depend on https://pypi.python.org to provide the file then the project maintainers may remove the version you are pinned to (causing your builds to fail - though some for some cowboys this will cause production deployments to fail).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pin the version of your dependencies, provide the dependencies locally or through a system under your control (your own pypi server or s3 bucket)&lt;/strong&gt; , explicit is better than implicit.&lt;/p&gt;
&lt;h3 id="wheels-are-better-python-packaging"&gt;Wheels are better Python Packaging&lt;/h3&gt;
&lt;p&gt;Wheels are (awesome), it's the beginning of trying to make python installations more deterministic and pip less dynamic at install time. &lt;a href="http://wheel.readthedocs.org/en/latest/"&gt;http://wheel.readthedocs.org/en/latest/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;An output directory of the wheels of a project are known by convention as a "wheelhouse".&lt;/p&gt;
&lt;p&gt;As a side effect the wheel directory, “/tmp/wheelhouse” in the example, contains installable copies of the exact versions of your application’s dependencies. By installing from those cached wheels you can recreate that environment quickly and with no surprises.&lt;/p&gt;
&lt;p&gt;When you install using pip it looks for a “wheel file” (*.whl which is the newer zip compressed format, goodbye .egg) of the correct name for your (virtual) environment (e.g. py2 or py3 or x86 linux).  This wheel file saves time and bugs from installing a package/.egg  from source (usually that time is spent compiling C code for the python library).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;wheel
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;projectsource
python&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;bdist_wheel
ls&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;./dist
pip&lt;span class="w"&gt; &lt;/span&gt;wheel&lt;span class="w"&gt;  &lt;/span&gt;--find-links&lt;span class="w"&gt; &lt;/span&gt;/root/wheelhouse&lt;span class="w"&gt; &lt;/span&gt;--wheel-dir&lt;span class="o"&gt;=&lt;/span&gt;/root/wheelhouse&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pip.pypa.io/en/stable/reference/pip_wheel/"&gt;https://pip.pypa.io/en/stable/reference/pip_wheel/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://pip-python3.readthedocs.org/en/latest/reference/pip_wheel.html#build-system-interface"&gt;http://pip-python3.readthedocs.org/en/latest/reference/pip_wheel.html#build-system-interface&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="installing-using-a-wheel-file"&gt;installing using a wheel file&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;somepackage&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;py2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;py3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;whl&lt;/span&gt;
&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;freeze&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="error-invalid-command-bdist_wheel"&gt;error: invalid command 'bdist_wheel'&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://pypi.python.org/pypi/docutils#downloads"&gt;https://pypi.python.org/pypi/docutils#downloads&lt;/a&gt; only provided a py3 wheel (facepalm)&lt;/p&gt;
&lt;p&gt;Downloading the source .tar.gz and running python setup.py bdist_wheel resulted in:
    error: invalid command 'bdist_wheel'&lt;/p&gt;
&lt;p&gt;Reading the internet provided no comprehensible answers (lots of "setuptools does not match your version of pip or wheel or whatever")&lt;/p&gt;
&lt;p&gt;The following hacking seems to have provided a solution:&lt;/p&gt;
&lt;p&gt;:::bash
    python --version
    pip --version
    cd /tmp
    wget https://pypi.python.org/packages/source/d/docutils/docutils-0.12.tar.gz#md5=4622263b62c5c771c03502afa3157768
    tar xf docutils-0.12.tar.gz
    cd docutils-0.12
    virtualenv venv
    source venv/bin/activate
    python --version
    pip --version
    pip install wheel
    pip freeze
    python setup.py install
    pip freeze
    pip wheel .
    ls /tmp/docutils-0.12/wheelhouse
        docutils-0.12-py2-none-any.whl&lt;/p&gt;</content><category term="build-CI-CD-devops"/><category term="python"/><category term="packaging"/><category term="pip"/><category term="wheels"/></entry><entry><title>Static site with Bitbucket and Shippable and Pelican</title><link href="https://blog.john-pfeiffer.com/static-site-with-bitbucket-and-shippable-and-pelican/" rel="alternate"/><published>2015-12-21T12:24:00-08:00</published><updated>2015-12-21T12:24:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2015-12-21:/static-site-with-bitbucket-and-shippable-and-pelican/</id><summary type="html">
&lt;h2 id="running-software-costs-money"&gt;Running Software Costs Money&lt;/h2&gt;
&lt;p&gt;One of the most overlooked costs in running a service is operations.  While Research and Development (aka coding) is often cited as the largest expense (software developer salaries! &lt;a href="https://www.quora.com/What-are-the-average-operating-costs-of-SaaS-companies"&gt;https://www.quora.com/What-are-the-average-operating-costs-of-SaaS-companies&lt;/a&gt;), and 80% (or more) of (successful) software's life is maintenance (&lt;a href="http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3610582/"&gt;http://www.ncbi …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">
&lt;h2 id="running-software-costs-money"&gt;Running Software Costs Money&lt;/h2&gt;
&lt;p&gt;One of the most overlooked costs in running a service is operations.  While Research and Development (aka coding) is often cited as the largest expense (software developer salaries! &lt;a href="https://www.quora.com/What-are-the-average-operating-costs-of-SaaS-companies"&gt;https://www.quora.com/What-are-the-average-operating-costs-of-SaaS-companies&lt;/a&gt;), and 80% (or more) of (successful) software's life is maintenance (&lt;a href="http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3610582/"&gt;http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3610582/&lt;/a&gt;), you have to run the darn thing all the time.&lt;/p&gt;
&lt;h2 id="a-free-and-efficient-static-web-site"&gt;A Free and Efficient Static Web Site&lt;/h2&gt;
&lt;p&gt;What is one cost effective (free!) and efficient solution to running a static site?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bitbucket also has a free static site capability (as long as the DNS is USERNAME.bitbucket.org), so no server/hosting required&lt;/li&gt;
&lt;li&gt;Bitbucket have free private repositories&lt;/li&gt;
&lt;li&gt;The Bitbucket static site repository can be private (only the html exposed will be visible to anonymous users)&lt;/li&gt;
&lt;li&gt;Shippable have a free plan with 1 container that will do your builds (fine by me, Docker is fast!)&lt;/li&gt;
&lt;li&gt;Pelican converts markdown into .html and you can still use javascript for fancy things &lt;a href="http://docs.getpelican.com/"&gt;http://docs.getpelican.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Sadly it looks like the Shippable startup is having trouble maintaining/updating their Docker images so I recommend instead to use Bitbucket Pipelines &lt;a href="https://blog.john-pfeiffer.com/continuous-delivery-with-bitbucket-pipelines-and-google-app-engine-deployment-and-the-storageobjectslist-error/#bitbucket-pipelines-configuration"&gt;https://blog.john-pfeiffer.com/continuous-delivery-with-bitbucket-pipelines-and-google-app-engine-deployment-and-the-storageobjectslist-error/#bitbucket-pipelines-configuration&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The basic process is to be triggered by a git push to the private repository of new/updated source markdown, use pelican to process it into .html, and then publish (git push) the new/updated .html to the static site repository.&lt;/p&gt;
&lt;p&gt;One reason to use two seperate repositories instead of only one repository is that if you make a commit to your markdown source repository that will trigger a CI run which will push the updated .html files to the repository which would be detected and maybe trigger an infinite loop.  Or at the least interleave your source code changes with generated output changes in the commit logs.  =]&lt;/p&gt;
&lt;p&gt;An alternative is using multiple branches but you'd better hope nobody ever deletes your source branch by accident.&lt;/p&gt;
&lt;p&gt;Another alternative is to include an IF statement in your shippable code to not push if the diff/md5 of the source files (or maybe check against the output .html?) still match.  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I say keep it super simple ;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="bitbucket-setup"&gt;Bitbucket setup&lt;/h2&gt;
&lt;h3 id="create-the-source-and-target-repositories"&gt;Create The Source and Target Repositories&lt;/h3&gt;
&lt;p&gt;Create a new private repository (for your markdown), consider prefixing the name with source or something (good names makes for good maintenance)&lt;/p&gt;
&lt;p&gt;Make sure you have cloned the pelican project and setup a basic static site: &lt;a href="https://blog.john-pfeiffer.com/how-to-set-up-a-pelican-static-blog-site/"&gt;https://blog.john-pfeiffer.com/how-to-set-up-a-pelican-static-blog-site/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Inside your .gitignore you will probably want to exclude .pyc and ./output and any other pelican created artifacts.&lt;/p&gt;
&lt;p&gt;Inside your repository at the root level you will need a shippable.yaml file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.7"&lt;/span&gt;
&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pelican&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Markdown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;beautifulsoup4&lt;/span&gt;
&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;pelican&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ahl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pelican&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;publishconf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;py&lt;/span&gt;
&lt;span class="n"&gt;after_script&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ahl&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ahl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This assumes that the pelican-project is a subdirectory in the repository using the best practice of leaving the top level of a repository for build and test artifacts and isolating the source code into a subdirectory.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Create another private repository for your public html.  It MUST be named USERNAME.bitbucket.org to make use of the bitbucket static site capabilities.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(yes, the name must include those dots/domain name of the service)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://confluence.atlassian.com/bitbucket/publishing-a-website-on-bitbucket-cloud-221449776.html"&gt;https://confluence.atlassian.com/bitbucket/publishing-a-website-on-bitbucket-cloud-221449776.html&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="get-oauth-access"&gt;Get OAuth Access&lt;/h3&gt;
&lt;p&gt;The goal here is to leverage the one-hour-access-token-generation-via-Oauth in order to write to a different repository in Bitbucket.&lt;/p&gt;
&lt;p&gt;Generate a Consumer OAuth2 Token with &lt;a href="https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html#OAuthonBitbucketCloud-Createaconsumer"&gt;https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html#OAuthonBitbucketCloud-Createaconsumer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(The UI in &lt;a href="https://bitbucket.org/account/user/USERNAME/api"&gt;https://bitbucket.org/account/user/USERNAME/api&lt;/a&gt; is fairly straightforward with "Add consumer")&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The least privilege required permissions would be "Repositories: Read, Write"
You must define a callback URL, i.e. http://YOURDOMAIN:8888 , even if it is not used by this workaround&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Use curl to verify your token (this is how Shippable will get a 1 hour expiring access token to work on the target output repository)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//bitbucket.org/site/oauth2/access_token -d grant_type=client_credentials -u yourkeyhere:yoursecrethere&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"abcd1234..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"scopes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"repository:write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The curl command should get a JSON response which includes an access_token&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="http://stackoverflow.com/questions/24965307/how-to-manipulate-bitbucket-repository-with-token"&gt;http://stackoverflow.com/questions/24965307/how-to-manipulate-bitbucket-repository-with-token&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="shippable-setup"&gt;Shippable setup&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Sadly it looks like the Shippable startup is having trouble maintaining/updating their Docker images so I recommend instead to use Bitbucket Pipelines &lt;a href="https://blog.john-pfeiffer.com/continuous-delivery-with-bitbucket-pipelines-and-google-app-engine-deployment-and-the-storageobjectslist-error/#bitbucket-pipelines-configuration"&gt;https://blog.john-pfeiffer.com/continuous-delivery-with-bitbucket-pipelines-and-google-app-engine-deployment-and-the-storageobjectslist-error/#bitbucket-pipelines-configuration&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Enable the integration with Bitbucket: &lt;a href="http://docs.shippable.com/#step-0-prerequisite"&gt;http://docs.shippable.com/#step-0-prerequisite&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Right from the beginning Shippable tries to ask which source repository provider (either GitHub or Bitbucket) you will be using.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Use Shippable's OAuth implementation (Account Integration) to pick which Bitbucket repository&lt;/p&gt;
&lt;p&gt;Home -&amp;gt; CI (dropdown) -&amp;gt; Bitbucket (hopefully you have a different avatar between Bitbucket and GitHub)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Press "Sync" if you have a newly created repository that is not listed yet&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Add the Bitbucket OAuth2 Key and Secret as a Shippable secure environment variable in the format KEY:SECRET&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Go to https://app.shippable.com/projects/1234d2ea1895ca4474661234/settings and look for the Encrypt section
Fill it in with something like OAUTH_USER=yourkeyhere:yoursecrethere&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="http://shippable-docs-20.readthedocs.org/en/latest/config.html#secure-environment-variables"&gt;http://shippable-docs-20.readthedocs.org/en/latest/config.html#secure-environment-variables&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Copy the output to your shippable.yml file&lt;/p&gt;
&lt;h2 id="putting-it-all-together"&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;Update the source repository top level shippable.yaml file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.7"&lt;/span&gt;
&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pelican&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Markdown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;beautifulsoup4&lt;/span&gt;
&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;yourencryptedkeyandsecret&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;pelican&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ahl&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ahl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pelican&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;publishconf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ahl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;oauth2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;grant_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client_credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;OAUTH_USER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BBTOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'import sys, json; print json.load(sys.stdin)["access_token"]'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://x-token-auth:$BBTOKEN==@bitbucket.org/USERNAME/USERNAME.bitbucket.org"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ahl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;USERNAME&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;USERNAME&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;USERNAME&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;../&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ahl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"me@example.com"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Pfeiffer"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"build $BUILD_NUMBER commit $COMMIT"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;../&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="n"&gt;after_script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ahl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Adding the bitbucket oauth2 consumer key and secret (separated by a colon) as an encrypted environment variable&lt;/li&gt;
&lt;li&gt;using curl to generate a temporary access_token and extracting it into a local environment variable&lt;/li&gt;
&lt;li&gt;cloning with the access)token and removing the previous contents and replacing them with the newly generated output&lt;/li&gt;
&lt;li&gt;leveraging the CI variables to indicate on the output html repository what markdown source commits triggered this build&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id="reviewing-the-output"&gt;Reviewing the output&lt;/h2&gt;
&lt;p&gt;The full output of the run is available at something like https://app.shippable.com/builds/1234dec1d00e020c0011234&lt;/p&gt;
&lt;p&gt;This is really helpful for debugging (especially seeing how many seconds each step took)&lt;/p&gt;
&lt;p&gt;Possible improvements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;use a python application best practice of documenting dependencies with a requirements.txt file at the top level&lt;/li&gt;
&lt;li&gt;putting all of the commands into a script like publish-in-ci.sh so that it could be run locally in a dev environment&lt;/li&gt;
&lt;li&gt;add the Dockerfile used for local development into the source repository to consolidate and simplify development in one place&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="misc"&gt;Misc&lt;/h2&gt;
&lt;p&gt;One thing that is interesting about this is that using OAuth tokens through a service is merely wrapping all of the manual steps I have in a previous blog post into a nice SaaS wrapper =)
&lt;a href="https://blog.john-pfeiffer.com/publish-a-pelican-blog-using-a-bitbucket-post-webhook/"&gt;https://blog.john-pfeiffer.com/publish-a-pelican-blog-using-a-bitbucket-post-webhook/&lt;/a&gt;&lt;/p&gt;</content><category term="build-CI-CD-devops"/><category term="static site"/><category term="pelican"/><category term="bitbucket"/><category term="shippable ci"/><category term="ci"/><category term="cd"/></entry><entry><title>Go Programming Intro with VS Code and Arrays Slices Functions and Testing</title><link href="https://blog.john-pfeiffer.com/go-programming-intro-with-vs-code-and-arrays-slices-functions-and-testing/" rel="alternate"/><published>2015-11-15T21:19:00-08:00</published><updated>2015-11-15T21:19:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2015-11-15:/go-programming-intro-with-vs-code-and-arrays-slices-functions-and-testing/</id><summary type="html">
&lt;p&gt;Introducing the Go Programming Language (aka golang) basics, interactive sandbox with &lt;a href="https://play.golang.org"&gt;https://play.golang.org&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Tooling is often essential to productivity.&lt;/p&gt;
&lt;h3 id="download-and-install-the-go-language-compiler-and-tools"&gt;Download and install the go language compiler and tools&lt;/h3&gt;
&lt;h4 id="vim-and-docker"&gt;vim and Docker&lt;/h4&gt;
&lt;p&gt;Or alternatively just avoid the IDE and Environment and use vim and a docker container ;) &lt;a href="https://hub.docker.com/_/golang/"&gt;https …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Introducing the Go Programming Language (aka golang) basics, interactive sandbox with &lt;a href="https://play.golang.org"&gt;https://play.golang.org&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Tooling is often essential to productivity.&lt;/p&gt;
&lt;h3 id="download-and-install-the-go-language-compiler-and-tools"&gt;Download and install the go language compiler and tools&lt;/h3&gt;
&lt;h4 id="vim-and-docker"&gt;vim and Docker&lt;/h4&gt;
&lt;p&gt;Or alternatively just avoid the IDE and Environment and use vim and a docker container ;) &lt;a href="https://hub.docker.com/_/golang/"&gt;https://hub.docker.com/_/golang/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="installing-and-the-environment"&gt;Installing and the Environment&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://golang.org/dl/"&gt;https://golang.org/dl/&lt;/a&gt; and &lt;code&gt;cd /opt; tar xf go.tar.gz&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Instead of the ephemeral &lt;code&gt;export PATH=$PATH:/usr/local/go/bin&lt;/code&gt; I prefer the persistent ~/.profile (or for all users /etc/profile though clearly /opt indicates a single user system ;)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Go Programming&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GOROOT&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=$&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;GOROOT&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;goprojects&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=$&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;

&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~/.&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The last step gets you going without needing to reload your shell, full docs at &lt;a href="https://golang.org/doc/install"&gt;https://golang.org/doc/install&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt; be careful how you name your executables as the $GOPATH/bin will contain the names of the projects as binaries (so don't create a project or binary named bash!)&lt;/p&gt;
&lt;h4 id="go-docs-locally"&gt;Go Docs locally&lt;/h4&gt;
&lt;p&gt;If you need the go standard library docs (and access to any of the code docs from repos in the path) you can run:
&lt;code&gt;godoc -http=:6060&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A local web server with documentation for the go standard library
&lt;a href="https://godoc.org/golang.org/x/tools/cmd/godoc"&gt;https://godoc.org/golang.org/x/tools/cmd/godoc&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="manual-go-cli-execution-and-compilation"&gt;Manual Go CLI execution and compilation&lt;/h3&gt;
&lt;p&gt;The traditional command line method is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;path&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;PROJECTNAME&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PROJECTNAME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This will run go in a "dev mode" where it pulls in dependencies and executes immediately (no artifacts are created)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To compile and install the binary into the local path:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;path&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;PROJECTNAME&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;PROJECTNAME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This compiles and builds and installs the binary into the $GOPATH&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To build a binary in the current directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;path&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;PROJECTNAME&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This will just build the binary locally (i.e. name.go becomes "name") in the current directory, not in the $GOPATH&lt;/p&gt;
&lt;p&gt;Be aware that sometimes you may forget and commit this new binary to version control (badpokerface)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org/doc/code.html#Command"&gt;https://golang.org/doc/code.html#Command&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dave.cheney.net/2014/01/21/using-go-test-build-and-install"&gt;https://dave.cheney.net/2014/01/21/using-go-test-build-and-install&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A concrete example:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Compile with: &lt;code&gt;cd /opt/goprojects/src/github.com/johnpfeiffer/intro/ ; go install&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Execute with: &lt;code&gt;/opt/goprojects/bin/intro&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href="https://golang.org/doc/code.html"&gt;https://golang.org/doc/code.html&lt;/a&gt; is a very complete tutorial, notes for myself:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat&lt;span class="w"&gt; &lt;/span&gt;~/.profile
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/Desktop/repos/goprojects
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:&lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/bin

&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/.profile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;an example of setting up the environment in Mac OSX&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If your files are laid out like this...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/Users/johnpfeiffer/Desktop/repos/goprojects
  - bin
  - pkg
  - src
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;mkdir -p $GOPATH/src/bitbucket.org/johnpfeiffer/myproject/mystrings&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;a wrinkle on the official go tutorial where both packages are in the same remote git repository&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;mystrings/mystrings.go&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mystrings&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;StringLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;A simple "library" package that can be re-used&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cd /tmp; go install bitbucket.org/johnpfeiffer/myproject/mystrings&lt;/code&gt;
ALTERNATIVELY: &lt;code&gt;cd $GOPATH/src/bitbucket.org/johnpfeiffer/myproject/mystrings; go install&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Installing the package builds the .a binary which can be used by other builds/programs&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;mkdir -p $GOPATH/src/bitbucket.org/johnpfeiffer/myproject/hello&lt;/code&gt;
&lt;strong&gt;hello/hello.go&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"bitbucket.org/johnpfeiffer/myproject/mystrings"&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mystrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StringLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;a simple main package that can be called from the CommandLineInterface&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cd $GOPATH/src/bitbucket.org/johnpfeiffer/myproject/hello; go install&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Execute your new program with: &lt;code&gt;$GOPATH/bin/hello&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(or since $GOPATH/bin is in $PATH, just type: &lt;code&gt;hello&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;An ASCII diagram of the file system&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;johnpfeiffer&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myproject&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mystrings&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mystrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="cross-compiling-with-go"&gt;Cross Compiling with Go&lt;/h4&gt;
&lt;p&gt;If developing on Mac OSX and wanted to compile/build a 64bit linux binary&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;johnpfeiffer&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myproject&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;
&lt;span class="n"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;linux&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;amd64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;internal&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;internal&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;atomic&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;runtime&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;johnpfeiffer&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myproject&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mystrings&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;johnpfeiffer&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myrpoject&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the "go build -v" extra flag outputs verbosely the intermediate steps&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;file hello
     ./hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;using the "file" command we can inspect it really has been built correctly in the current directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Dave Cheney's suggestion about "build vs install" is a good one since the cached intermediate .a files in the directory pkg/ may complicate things, especially for a cross compilation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org/doc/install/source#environment"&gt;https://golang.org/doc/install/source#environment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dave.cheney.net/2015/08/22/cross-compilation-with-go-1-5"&gt;https://dave.cheney.net/2015/08/22/cross-compilation-with-go-1-5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="download-and-install-an-ide"&gt;Download and install an IDE&lt;/h3&gt;
&lt;p&gt;If you are used to larger projects then an IDE is quite helpful for colorization, auto completion, (right click or f12) goto definition, rename, build on save, auto formatting, etc.&lt;/p&gt;
&lt;p&gt;Suprisingly one of the most popular and effective Go IDE combinations is: &lt;a href="https://code.visualstudio.com/Docs/?dv=linux64_deb"&gt;https://code.visualstudio.com/Docs/?dv=linux64_deb&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;dpkg -i code_...amd64.deb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To install &lt;a href="https://marketplace.visualstudio.com/items?itemName=lukehoban.Go"&gt;https://marketplace.visualstudio.com/items?itemName=lukehoban.Go&lt;/a&gt; aka &lt;a href="https://github.com/Microsoft/vscode-go"&gt;https://github.com/Microsoft/vscode-go&lt;/a&gt; you actually...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The plugin has been renamed to "ms-vscode.go" =|&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;open Visual Studio Code&lt;/li&gt;
&lt;li&gt;Control + P (Launches VS Code Quick Open)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ext install ms-vscode.Go&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;restart when prompted&lt;/li&gt;
&lt;li&gt;File -&amp;gt; Preferences -&amp;gt; Color Theme (Light Visual Studio ;)&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;At this point you probably need to reboot to get VSCode to recognize the GOPATH correctly&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In your shell where you setup the GOPATH run the following to get all of the analysis tools that "Go for Visual Studio Code" uses, though it's a lot easier to just use the IDE in the bottom right corner "Analysis Tools Missing"&lt;/p&gt;
&lt;p&gt;To take advantage of those tools (like gofmt on save), in your workspace (GOPATH) there will be a .vscode directory with settings.json &lt;a href="https://code.visualstudio.com/docs/customization/userandworkspace"&gt;https://code.visualstudio.com/docs/customization/userandworkspace&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Place your settings in this file to overwrite default and user settings.&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"go.buildOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"go.lintOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"go.vetOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"go.buildFlags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"go.lintFlags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"go.vetFlags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"go.coverOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"go.useCodeSnippetsOnFunctionSuggest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"go.formatOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"go.formatTool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"goreturns"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Just in case restart VSCode to recognize the updated settings&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Unfortunately it is not quite simple to execute the code directly in VSCode &lt;a href="https://github.com/Microsoft/vscode-go/issues/21"&gt;https://github.com/Microsoft/vscode-go/issues/21&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="vs-code-preferences-to-disable-telemetry-and-automatic-updates"&gt;VS Code preferences to disable telemetry and automatic updates&lt;/h4&gt;
&lt;p&gt;To prevent the software from sending (some) data to Microsoft...&lt;/p&gt;
&lt;p&gt;File -&amp;gt; Preferences -&amp;gt; Settings&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;"telemetry.enableTelemetry": false,
"telemetry.enableCrashReporter": false,
"update.channel": "none"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;disabling sending stats and crash reports to vortex.data.microsoft.com and also prevent checking for software updates&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/Docs/supporting/FAQ#_how-to-disable-telemetry-reporting"&gt;https://code.visualstudio.com/Docs/supporting/FAQ#_how-to-disable-telemetry-reporting&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="custom-vs-code-icons-and-disabling-annoying-file-icons"&gt;Custom VS Code icons and disabling annoying file icons&lt;/h4&gt;
&lt;p&gt;There is a feature to show file icons at the top of every file (i.e. distinguish visually/graphically between Go source and HTML)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://code.visualstudio.com/docs/getstarted/themes#_icon-themes"&gt;https://code.visualstudio.com/docs/getstarted/themes#_icon-themes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To graphically disable/modify the file icons: &lt;code&gt;File -&amp;gt; Preferences -&amp;gt; File Icon Theme and choose "None"&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;To disable the theme using the settings file at the User level (or workspace level if you want to have to modify this for every project):&lt;/p&gt;
&lt;p&gt;File -&amp;gt; Preferences -&amp;gt; Settings&lt;/p&gt;
&lt;p&gt;USER SETTINGS (settings.json)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;"workbench.iconTheme": null,
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;(previously it was vs-seti , apparently Seti is the poor rendition of gophers as ugly brown blobs)&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;"workbench.colorTheme": "Visual Studio Light",
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;because simple text with a light background is actually easier on the eyes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://code.visualstudio.com/docs/getstarted/settings"&gt;https://code.visualstudio.com/docs/getstarted/settings&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="disabling-the-minimap-enhanced-scroll-bar"&gt;Disabling the minimap enhanced scroll bar&lt;/h4&gt;
&lt;p&gt;It is pretty nifty to see the scroll bar have colors/graphics indicating roughly where in the file you are.&lt;/p&gt;
&lt;p&gt;If you want to maximize your available editing width then you just want a "normal" scroll bar (or removed entirely because "clean code" your files are small ;)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;File -&amp;gt; Preferences -&amp;gt; Setting&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In USER SETTINGS add the line to the settings.json that the IDE opened for you (in proper JSON syntax):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;"editor.minimap.enabled": false
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="pro-tip-to-avoid-vs-code-melting-your-system-down"&gt;Pro tip to avoid VS Code melting your system down&lt;/h4&gt;
&lt;p&gt;If you find your machine suddenly slows down terribly with VS Code open and that CPU, then RAM, then finally swap (kswapd0) then you have probably run into an annoying trap:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;share&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;share&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;bootstrap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;watcherService&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That high cpu utilization is either the project file watcher indexing or one of the language plugins (because "open source") attempting to lint every file in the project.&lt;/p&gt;
&lt;p&gt;That's right, if you have a large git repository, a temp directory with some test data, or anything else it can get it reach Visual Studio Code will scan it at the expense of your machine.&lt;/p&gt;
&lt;p&gt;(and it may even be scanning all sorts of random locations throughout your file system (stare))&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/Microsoft/vscode/issues/3998"&gt;https://github.com/Microsoft/vscode/issues/3998&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The following may have helped (but more likely is that I moved the large files to another directory outside of the Project):&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;settings.json&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;"files.watcherExclude": {
    "**/.git/objects/**": true,
    "**/.git/subtree-cache/**": true,
    "**/node_modules/**": true,
    "**/*.aes": true,
    "**/TEMP": true
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="install-or-build-or-run"&gt;Install or Build or Run&lt;/h3&gt;
&lt;p&gt;Because Go is a static language there is a compilation (and linking) phase where the source code is transformed into a binary.&lt;/p&gt;
&lt;h4 id="using-visual-studio-code-tasks-to-build-go"&gt;Using Visual Studio Code Tasks to Build Go&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;With your main.go file open press &lt;code&gt;Control + Shift + B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;"No task runner configured" -&amp;gt; click on "Configure Task Runner"&lt;/li&gt;
&lt;li&gt;From the dropdown choose "Others"&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;fill in tasks.json&lt;/p&gt;
&lt;p&gt;{
    "version": "0.1.0",
    "command": "pwd",
    "isShellCommand": true,
    "showOutput": "always"
}&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The command will execute in the folder that is opened in "File -&amp;gt; Open Folder"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;{
    "version": "0.1.0",
    "command": "go",
    "isShellCommand": true,
    "args": ["build", "-v"],
    "showOutput": "always"
}&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This builds verbosely in the current directory, assuming that the IDE has opened the project folder since Go only uses relative path &lt;a href="https://golang.org/ref/spec#ImportPath"&gt;https://golang.org/ref/spec#ImportPath&lt;/a&gt; (and the GOPATH is set correctly)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you prefer to File -&amp;gt; Open Folder a top level folder that has many subfolders with go projects then...&lt;/p&gt;
&lt;p&gt;The workaround for not having "change working directory" or "command chaining" is to create a shell script, build.sh&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nb"&gt;pwd&lt;/span&gt;
ls&lt;span class="w"&gt; &lt;/span&gt;-l
go&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;-v
ls&lt;span class="w"&gt; &lt;/span&gt;-l
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The script could be reduced to "cd $1; go build -v" but having the extra debugging output can help...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The task runner must be defined to take advantage of the new script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{
&lt;span class="w"&gt;    &lt;/span&gt;"version":&lt;span class="w"&gt; &lt;/span&gt;"0.1.0",
&lt;span class="w"&gt;    &lt;/span&gt;"taskName":&lt;span class="w"&gt; &lt;/span&gt;"build",
&lt;span class="w"&gt;    &lt;/span&gt;"command":&lt;span class="w"&gt; &lt;/span&gt;"bash",
&lt;span class="w"&gt;    &lt;/span&gt;"args":&lt;span class="w"&gt; &lt;/span&gt;["&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/build.sh",&lt;span class="w"&gt; &lt;/span&gt;"&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;fileDirname&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;"],
&lt;span class="w"&gt;    &lt;/span&gt;"isShellCommand":&lt;span class="w"&gt; &lt;/span&gt;true
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The IDE stores the files in PROJECTFOLDER/.vscode/tasks.json&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://code.visualstudio.com/Docs/editor/tasks#_variable-substitution"&gt;https://code.visualstudio.com/Docs/editor/tasks#_variable-substitution&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="using-visual-studio-code-tasks-to-run-go"&gt;Using Visual Studio Code Tasks to Run Go&lt;/h4&gt;
&lt;p&gt;You can continue to create more key bindings (hotkeys) for running Go or running Tests, I will just override the Build (Control + Shift + B) hotkey for now...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;go&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;*.go
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This does not build and instead just executes directly&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{
&lt;span class="w"&gt;    &lt;/span&gt;"version":&lt;span class="w"&gt; &lt;/span&gt;"0.1.0",
&lt;span class="w"&gt;    &lt;/span&gt;"taskName":&lt;span class="w"&gt; &lt;/span&gt;"build",
&lt;span class="w"&gt;    &lt;/span&gt;"command":&lt;span class="w"&gt; &lt;/span&gt;"bash",
&lt;span class="w"&gt;    &lt;/span&gt;"args":&lt;span class="w"&gt; &lt;/span&gt;["&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;/run.sh",&lt;span class="w"&gt; &lt;/span&gt;"&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;fileDirname&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;"],
&lt;span class="w"&gt;    &lt;/span&gt;"isShellCommand":&lt;span class="w"&gt; &lt;/span&gt;true
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;this extra indirection of a separate run.sh is only necessary if you prefer having a top level "meta" folder&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://code.visualstudio.com/Docs/customization/keybindings#_tasks"&gt;https://code.visualstudio.com/Docs/customization/keybindings#_tasks&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="go-test-with-a-visual-studio-code-test-task"&gt;Go Test with a Visual Studio Code Test Task&lt;/h4&gt;
&lt;p&gt;The "test" task is unassigned (at least for my linux installation) so the first step is to inspect and customize the hotkey keybindings.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Control+K Control+S&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Or use File -&amp;gt; Preferences -&amp;gt; Keyboard Shortcuts&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the left pane search the "Default Keybindings" file for "build", i.e. on line 502 you should see:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{ "key": "ctrl+shift+b",          "command": "workbench.action.tasks.build" },
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Add to the empty "keybindings.json" on the right a new hotkey&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;[
    { "key": "ctrl+shift+t",          "command": "workbench.action.tasks.test" }
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Save the file (the IDE will store your customization in ~/.config/Code/User/keybindings.json)&lt;/p&gt;
&lt;p&gt;Now when you use &lt;code&gt;Control+Shift+T&lt;/code&gt; you should see "No task configured" and "Configure Task Runner"&lt;/p&gt;
&lt;p&gt;This example defines multiple tasks, both go build and go test (this will save to .vscode/tasks.json)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="ss"&gt;"version"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;"0.1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="ss"&gt;"tasks"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
&lt;span class="n"&gt;        {&lt;/span&gt;
&lt;span class="n"&gt;            "taskName": "build",&lt;/span&gt;
&lt;span class="n"&gt;            "command": "go",&lt;/span&gt;
&lt;span class="n"&gt;            "args": ["build", "-v"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="ss"&gt;"isShellCommand"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="ss"&gt;"showOutput"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;"always"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="ss"&gt;"taskName"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="ss"&gt;"command"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;"go"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="ss"&gt;"args"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;"test", "-v"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="ss"&gt;"isShellCommand"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="ss"&gt;"showOutput"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;"always"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Assuming you have opened File -&amp;gt; Open Folder at the top level of your code tree as Go expects relative paths,&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;Control+Shift+T&lt;/code&gt; will now open the Tasks output pane and display the results of any tests run&lt;/p&gt;
&lt;h4 id="debugging-with-delve"&gt;Debugging with Delve&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;derekparker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;delve&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dlv&lt;/span&gt;
&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;goprojects&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;derekparker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;delve&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dlv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first time I attempted to do it manually:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -ahl /opt/goprojects/bin/
cd /opt/goprojects/src/github.com/johnpfeiffer/YOURPROJECT
/opt/goprojects/bin/dlv  debug --headless --listen=:2345 --log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now in the VSCode IDE open the project folder and create your helloworld.go source file and Control + S to save (and auto gofmt) and then press &lt;strong&gt;F5&lt;/strong&gt; and it will connect to the Delve Debugger and display the output&lt;/p&gt;
&lt;h4 id="delve-debugging-and-running-your-application-with-f5-is-automatic-once-installed-correctly"&gt;Delve Debugging and running your application with F5 is automatic once installed correctly&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;The first time you run "Continue" with F5 on a file it will prompt you to setup your launch.json (and the IDE will open the default template for you)&lt;/li&gt;
&lt;li&gt;Use the IDE to go back to your source .go file and press F5 again, this time since the .vscode subdirectory was created and the default delve launch.json file was created, it will just start in debug mode with the Debug Console output at the bottom&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="coding-and-compiling"&gt;Coding and Compiling&lt;/h2&gt;
&lt;p&gt;For VSCode IDE keyboard shortcuts: &lt;a href="https://code.visualstudio.com/docs/customization/keybindings"&gt;https://code.visualstudio.com/docs/customization/keybindings&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="comments-types-strings-slices-for-loops"&gt;Comments Types Strings Slices For Loops&lt;/h3&gt;
&lt;p&gt;The main package it where the execution begins (aka "main" in c &lt;a href="https://en.wikipedia.org/wiki/Entry_point"&gt;https://en.wikipedia.org/wiki/Entry_point&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Comments are either single line with double slashes or block comments &lt;a href="https://golang.org/doc/effective_go.html#commentary"&gt;https://golang.org/doc/effective_go.html#commentary&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="introgo"&gt;intro.go&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="cm"&gt;/* https://golang.org/doc/effective_go.html#mixed-caps&lt;/span&gt;
&lt;span class="cm"&gt;   https://golang.org/ref/spec#Constants */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;alphabetMax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// while&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"break or return exits an infinite loop"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// arrays are contiguous memory, fixed size and type&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// initialized to capacity 5 with values inserted, alternatively just initialized to empty with: var a [5]string&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"e"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// prefer Slices which are Reference Objects that wrap the underlying arrays&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// https://blog.golang.org/go-slices-usage-and-internals&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// cleaner way of iterating over key and value&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// []&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[:]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// [a b c d e]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// [c d e]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="arrays-are-contiguous-memory-and-4-bytes-is-normal"&gt;arrays are contiguous memory and 4 bytes is normal&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// int is usually the 4 byte int32 https://golang.org/ref/spec#Numeric_types&lt;/span&gt;
&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// dereference the addresses that are holding the values 1 and 2&lt;/span&gt;
&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d %d\n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;// rune is also int32&lt;/span&gt;
&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;rune&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sc"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'ä'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d %d\n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="fizzbuzz-and-switch"&gt;fizzbuzz and switch&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://golang.org/ref/spec#Switch_statements"&gt;https://golang.org/ref/spec#Switch_statements&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// usually static case values and "switch i {" , note it will NOT fall through by default&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fizzbuzz"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fizz"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"buzz"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="time"&gt;time&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="c1"&gt;// https://golang.org/pkg/time/#Now&lt;/span&gt;
&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"local:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UnixNano&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"ms"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"in UTC:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UTC&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UnixDate&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="packages-and-string-reverse"&gt;Packages and String Reverse&lt;/h3&gt;
&lt;p&gt;When you modularize your code into packages then multiple programs can make use of DRY &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="main"&gt;main&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"github.com/johnpfeiffer/mystringutil"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stringutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"!oG ,olleH"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="package-mystringutil-with-reverse"&gt;package mystringutil with Reverse&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Package mystringutil contains utility functions for working with strings. "go build"&lt;/span&gt;
&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mystringutil&lt;/span&gt;

&lt;span class="c1"&gt;// Reverse returns its argument string reversed rune-wise left to right.&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nb"&gt;rune&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="palindrome-and-string-conversion-integer-to-ascii"&gt;palindrome and string conversion integer to ascii&lt;/h3&gt;
&lt;p&gt;Besides the main function for executing you will obviously create re-usable packages which will contain functions.&lt;/p&gt;
&lt;p&gt;Here is the source code for a simple "is this string a palindrome" and "is this integer a palindrome" programs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"strconv"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPalindrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPalindrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ala"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPalindrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"noon"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPalindrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ab"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPalindrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"racecar"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPalindrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"abfooba"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// slower and uses extra memory&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPalindrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strconv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Itoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1991&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPalindrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strconv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Itoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1981&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// takes advantage of math (mindblown)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reverseInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"is a palindrome: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reverseInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"is a palindrome: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;121&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reverseInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"is a palindrome: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;isPalindrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;oppositeIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;opposite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;rune&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;oppositeIndex&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d %T %c compared to %d %c \n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;oppositeIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;opposite&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;oppositeIndex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;opposite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reverseInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;remainder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;remainder&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remainder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reversed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="random-integers"&gt;Random Integers&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://golang.org/pkg/crypto/rand/&lt;/span&gt;
&lt;span class="c1"&gt;// https://golang.org/pkg/math/big/&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;big&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Int&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myRandom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;big&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Int&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SetUint64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;myRandom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Reader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;myRandom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="binary-search"&gt;Binary Search&lt;/h3&gt;
&lt;p&gt;Using main and print is the poor man's Unit Testing ;)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"bytes"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;//todo pass by ref?&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;binarySearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;low&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"low ="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;low&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"mid ="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"high ="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;low&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;low&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;low&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;low&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;binarySearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;low&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;targets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;targets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;binarySearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"at location"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="efficient-string-append-and-replacement"&gt;Efficient String append and replacement&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/1760757/how-to-efficiently-concatenate-strings-in-go"&gt;https://stackoverflow.com/questions/1760757/how-to-efficiently-concatenate-strings-in-go&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myReplace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%20"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="testing-with-go"&gt;Testing with Go&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/testing/"&gt;https://golang.org/pkg/testing/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nathanleclaire.com/blog/2015/10/10/interfaces-and-composition-for-effective-unit-testing-in-golang/"&gt;https://nathanleclaire.com/blog/2015/10/10/interfaces-and-composition-for-effective-unit-testing-in-golang/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/appengine/docs/go/tools/localunittesting/#Go_Introducing_the_Go_testing_package"&gt;https://cloud.google.com/appengine/docs/go/tools/localunittesting/#Go_Introducing_the_Go_testing_package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="a-simple-web-server"&gt;A simple web server&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"hi"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Verify with &lt;code&gt;curl localhost:8080&lt;/code&gt;&lt;/p&gt;
&lt;h4 id="deploying-a-go-web-application-to-google-appengine"&gt;Deploying a Go Web Application to Google AppEngine&lt;/h4&gt;
&lt;p&gt;First create an &lt;strong&gt;app.yaml&lt;/strong&gt; file in your package&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MyApplicationName&lt;/span&gt;
&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;
&lt;span class="n"&gt;api_version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;go1&lt;/span&gt;

&lt;span class="n"&gt;handlers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/.*&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_go_app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Second adapt your source code to the Google App Engine entrypoint:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myapplicationname&lt;/span&gt;
&lt;span class="c1"&gt;// package main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"hi"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// The App Engine PaaS provides its own main() that handles the Listening and Serving ;)&lt;/span&gt;
&lt;span class="c1"&gt;//func main() {&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;//      http.ListenAndServe(":8080", nil)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Assuming you have created the project with &lt;a href="https://console.cloud.google.com/"&gt;https://console.cloud.google.com/&lt;/a&gt; and received a unique application id...&lt;/p&gt;
&lt;p&gt;A prerequisite is to use the SDK if you want to test it locally: &lt;a href="https://cloud.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go"&gt;https://cloud.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;unzip go_appengine_sdk...zip
/opt/go_appengine/goapp serve /path-to-project/MyProjectFolder/

  INFO     2016-06-02 06:30:27,493 devappserver2.py:769] Skipping SDK update check.
  INFO     2016-06-02 06:30:27,527 api_server.py:205] Starting API server at: http://localhost:38837
  INFO     2016-06-02 06:30:27,530 dispatcher.py:197] Starting module "default" running at: http://localhost:8080
  INFO     2016-06-02 06:30:27,531 admin_server.py:116] Starting admin server at: http://localhost:8000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="to-deploy-to-google-app-engine"&gt;To deploy to Google App Engine&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/opt/go_appengine/appcfg.py -A MyApplicationID update ./MyProjectFolder/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;View the version deployed and stats with &lt;a href="https://console.cloud.google.com/appengine/versions?project=MyApplicationId"&gt;https://console.cloud.google.com/appengine/versions?project=MyApplicationId&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl http://MyApplicationId.appspot.com/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;A gotcha is future updates deployed need the app.yaml version to increment AND either use the Web UI to set the new "default" or...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/opt/go_appengine/appcfg.py -A MyApplicationId set_default_version /MyProjectFolder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="handlerfunc-and-anonymous-functions-and-closure"&gt;HandlerFunc and Anonymous Functions and Closure&lt;/h3&gt;
&lt;p&gt;The decorator pattern Anonymous functions an&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="cm"&gt;/* using an anonymous function and closure to wrap the HandlerFunc&lt;/span&gt;
&lt;span class="cm"&gt;https://golang.org/pkg/net/http/#HandlerFunc&lt;/span&gt;
&lt;span class="cm"&gt;https://medium.com/@matryer/the-http-handlerfunc-wrapper-technique-in-golang-c60bf76e6124&lt;/span&gt;
&lt;span class="cm"&gt;*/&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;makeHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HandlerFunc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// https://golang.org/src/net/http/request.go&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"serving: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;h1&amp;gt;%s&amp;lt;/h1&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"starting..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;indexHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;makeHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Index"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;makeHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// https://golang.org/pkg/net/http/#HandleFunc , string, func(ResponseWriter, *Request)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;indexHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/john"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="more-info"&gt;More Info&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tour.golang.org/basics/"&gt;https://tour.golang.org/basics/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.joshsoftware.com/2014/03/12/learn-to-build-and-deploy-simple-go-web-apps-part-one/"&gt;https://blog.joshsoftware.com/2014/03/12/learn-to-build-and-deploy-simple-go-web-apps-part-one/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="programming"/><category term="go"/><category term="golang"/><category term="vscode"/><category term="testing"/><category term="arrays"/><category term="slices"/><category term="binary search"/></entry><entry><title>Build Automation using packer to build an AMI use immutable not chef</title><link href="https://blog.john-pfeiffer.com/build-automation-using-packer-to-build-an-ami-use-immutable-not-chef/" rel="alternate"/><published>2015-07-06T20:40:00-07:00</published><updated>2015-07-06T20:40:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2015-07-06:/build-automation-using-packer-to-build-an-ami-use-immutable-not-chef/</id><summary type="html">
&lt;h3 id="why-build-automation"&gt;Why build automation?&lt;/h3&gt;
&lt;p&gt;Software has always been about automation and leveraging the computer's capacity for precision and repetition.&lt;/p&gt;
&lt;p&gt;Somehow though, software is sometimes still deployed using a series of often poorly documented steps (to physical hardware even!). I've been there, it ain't pretty. (badpokerface)&lt;/p&gt;
&lt;p&gt;The second time you need to …&lt;/p&gt;</summary><content type="html">
&lt;h3 id="why-build-automation"&gt;Why build automation?&lt;/h3&gt;
&lt;p&gt;Software has always been about automation and leveraging the computer's capacity for precision and repetition.&lt;/p&gt;
&lt;p&gt;Somehow though, software is sometimes still deployed using a series of often poorly documented steps (to physical hardware even!). I've been there, it ain't pretty. (badpokerface)&lt;/p&gt;
&lt;p&gt;The second time you need to build a server running service(s) you may be under time pressure. (Murphy's law says you might be building it again because the first one which was business critical blew up unexpectedly.)&lt;/p&gt;
&lt;p&gt;Building things by hand is possibly the most expensive way to generate impossible to reproduce bugs and job security for the personality challenged.&lt;/p&gt;
&lt;p&gt;(Almost everyone agrees that technology employees are expensive and so by extension their time is constantly being wasted by everything they do).&lt;/p&gt;
&lt;p&gt;As virtualization (and linux!) took over the world there was an explosion of virtual machines that needed to be deployed and an evolution of a fairly standard virtual harwdare layer. (x86 cpu and Intel NIC anyone?)&lt;/p&gt;
&lt;p&gt;Suddenly you couldn't hire enough antisocial people to run around with floppies and scratching cds while shoving them into servers.&lt;/p&gt;
&lt;h3 id="why-not-chef"&gt;Why not chef?&lt;/h3&gt;
&lt;p&gt;Chef, Puppet, and Ansible are the well known configuration management and build/deployment automation tools.&lt;/p&gt;
&lt;p&gt;Automated configuration management which tries to keep a remote server in a specific state seems like a good recipe for things going wrong&lt;/p&gt;
&lt;p&gt;I've used chef successfully quite a few times and the main things that make it a specialized tool that I prefer not to use:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It's really easy to do chef wrong: nested roles and recipes that keep exploding exponentially with circular dependencies which make you think software development starts looking easy again.&lt;/li&gt;
&lt;li&gt;Community cookbooks are written to allow deployment on every architecture ever created (Debian, Ubuntu, RedHat, Windows, SPARC, etc.) which makes them challenging to read and debug, almost impossible to customize to do what you actually want.&lt;/li&gt;
&lt;li&gt;The ruby based DSL isn't bad but it's pretty annoying to constantly make syntax errors (which unless you're all TDD rambo and use Kitchen you'll find during the never ending waiting many minutes for a deployment to fail)&lt;/li&gt;
&lt;li&gt;It's difficult to debug the non intuitive "compilation phase" and "execution phase" way chef does its dependency tree magic, and the "shoot yourself in the foot" is compounded with the apparently edge case necessary compile time run executions&lt;/li&gt;
&lt;li&gt;The "best practices" have changed 3 or 4 times (write your own custom cookbooks, leverage the community cookbooks, write a custom wrapper for the community cookbook, don't ever use set_unless even though it still exists, etc.) and the 6 layers of variable overrides makes it hard to keep track of what the actual output of a script will be (don't worry, they have pages of documentation explaining it)&lt;/li&gt;
&lt;li&gt;The recommended "chef client server architecture" does not scale to really large numbers well and creates administration overhead and a lot of authorization complexity - and my preferred method with "chef solo" still requires an annoying amount of bootstrap setup on the target machines.&lt;/li&gt;
&lt;li&gt;Polling not only creates network congestion but worse creates windows of uncertainty about deployment state and the possibility of nodes silently dropping out &lt;a href="https://docs.chef.io/chef_client.html"&gt;https://docs.chef.io/chef_client.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Chef tends to encourage the pattern of long lived mutable servers (with their therefore necessary expensive and obnoxious biological caretakers)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.chef.io/resource_common.html#lazy-evaluation"&gt;https://docs.chef.io/resource_common.html#lazy-evaluation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.chef.io/resource_common.html#run-in-compile-phase"&gt;https://docs.chef.io/resource_common.html#run-in-compile-phase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://erik.hollensbe.org/2013/03/16/the-chef-resource-run-queue/"&gt;http://erik.hollensbe.org/2013/03/16/the-chef-resource-run-queue/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So is there a simpler way to just reliably, reproducibly, build a box?&lt;/p&gt;
&lt;h3 id="packer-to-the-rescue"&gt;Packer to the rescue&lt;/h3&gt;
&lt;p&gt;Packer is from the same people who brought you Vagrant &lt;a href="https://www.vagrantup.com/"&gt;https://www.vagrantup.com/&lt;/a&gt; , that really easy way to set up a virtual machine... &lt;a href="https://blog.john-pfeiffer.com/using-vagrant-to-deploy-instances-on-aws"&gt;https://blog.john-pfeiffer.com/using-vagrant-to-deploy-instances-on-aws&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It is very straightforward to read and actually you can still leverage chef (unless you realize that a series of shell commands is all you wanted anyways...)&lt;/p&gt;
&lt;p&gt;This leads to the better path of "immutable servers" &lt;a href="http://martinfowler.com/bliki/ImmutableServer.html"&gt;http://martinfowler.com/bliki/ImmutableServer.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;packer --version&lt;/code&gt;&lt;/p&gt;
&lt;h4 id="my_example_boxjson"&gt;my_example_box.json&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"variables"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"aws_access_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"aws_secret_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"builders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amazon-ebs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"access_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{user `aws_access_key`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"secret_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{user `aws_secret_key`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"source_ami"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ami-de0d9eb7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"instance_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t1.micro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"ssh_username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"ami_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"packer-example {{timestamp}}"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;packer validate mybox.json&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;packer build mybox.json&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;packer build -debug mybox.json&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This will prompt for the enter key to continue at each step&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once it's done it will terminate the EC2 instance for you (it only runs as long as it takes to build the machine and then burn the Amazon Machine Image).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;us-east-1: ami-19601234&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Unfortunately it is not machine readable json output so you have to do some bash-fu to extract just the id&lt;/p&gt;
&lt;p&gt;Also unfortunately there is no way to tell packer to not terminate so you can troubleshoot, the workarounds are the -debug which is essentially "interactive" or adding sleep commands&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="my_advanced_boxjson"&gt;my_advanced_box.json&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"_comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This is a comment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"variables"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;"my_secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{env `MY_SECRET`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"builders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amazon-ebs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"source_ami"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ami-de0d9eb7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"instance_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t1.micro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"ssh_username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"ami_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;"packer-example {{timestamp}}"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"subnet_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"subnet-f0be1234"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"security_group_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sg-9bf51234"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"associate_public_ip_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"ssh_keypair_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-packer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"ssh_private_key_file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./my-packer.pem"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;"provisioners"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./debs/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/tmp"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;"inline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"/sbin/ip a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"curl -s http://checkip.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"ls -ahl /tmp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"echo {{user `my_secret`}} &amp;gt; /tmp/{{isotime \"2006-01-02-030405\"}}--my-secret.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"sudo dpkg -i --force-confnew /tmp/*.deb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"machine_state_validation.sh"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The access credentials could instead be environment variables: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY&lt;/p&gt;
&lt;p&gt;Post instantiation validation is a really handy safeguard as statistically something always goes wrong somewhere and it's far cheaper to find out with a quick test versus a system that loses data.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="packer-and-digitalocean"&gt;Packer and DigitalOcean&lt;/h3&gt;
&lt;p&gt;DigitalOcean is a relatively new player (compared to Linode and even AWS) but they provide a very fast and easy to use way of building boxes (a snapshot can be used like an AMI to spin up multiple instances).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"_comment"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.packer.io/docs/builders/digitalocean.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"variables"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"digitalocean_api_token"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{env `DIGITALOCEAN_API_TOKEN`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"newuser_name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{env `NEWUSER_NAME`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"newuser_password"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{env `NEWUSER_PASSWORD`}}"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"builders"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"digitalocean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"api_token"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{user `digitalocean_api_token`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"size"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"512mb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"region"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lon1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"image"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ubuntu-16-04-x64"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"droplet_name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"built-from-packer-{{timestamp}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"snapshot_name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"built-from-packer-{{timestamp}}"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;}]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"provisioners"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"inline"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"ip a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"curl -s http://checkip.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"apt-get update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"sudo apt-get install -y vim curl wget byobu ntp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"timedatectl set-timezone Etc/UTC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"cat /etc/timezone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"useradd -s /bin/bash -m {{user `newuser_name`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"usermod -a -G admin {{user `newuser_name`}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"echo '{{user `newuser_name`}}:{{user `newuser_password`}}'|chpasswd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"cat /etc/passwd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"sed -i 's/Port 22/Port 2222/g' /etc/ssh/sshd_config"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"sed -i 's/PermitRootLogin yes/PermitRootLogin no/g' /etc/ssh/sshd_config"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"echo 'PasswordAuthentication no' &amp;gt;&amp;gt; /etc/ssh/sshd_config"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"cat /etc/ssh/sshd_config"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"mkdir -p /home/{{user `newuser_name`}}/.ssh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;"mkdir -p /opt/www/html"&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"authorized_keys"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"destination"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/{{user `newuser_name`}}/.ssh/authorized_keys"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;

&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a simple example that automates some of the security best practices of a non standard username, non standard ssh port, no ssh root login, no ssh password based login, etc.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;NEWUSER_NAME=yourusername NEWUSER_PASSWORD=yourpassword DIGITALOCEAN_API_TOKEN=012345yourtoken /opt/packer build packer.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="why-not-docker-containers"&gt;Why not docker containers?&lt;/h3&gt;
&lt;p&gt;Actually I prefer docker containers as the artifact and deployment vehicle for services but it's not the only hammer in your toolbelt.  And you have to setup the Docker hosts somehow, right? &lt;/p&gt;
&lt;p&gt;(Unless you've already uploaded your soul into the matrix and are using Googazon's PaaS and never have to sully your container delicate fingers with a crude virtual machine again).&lt;/p&gt;
&lt;h3 id="more-info"&gt;more info&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;https://www.packer.io/docs/installation.html&lt;/li&gt;
&lt;li&gt;https://www.packer.io/docs/builders/amazon-ebs.html&lt;/li&gt;
&lt;li&gt;https://www.packer.io/intro/getting-started/build-image.html&lt;/li&gt;
&lt;li&gt;https://www.packer.io/docs/templates/configuration-templates.html&lt;/li&gt;
&lt;li&gt;https://www.packer.io/docs/templates/user-variables.html&lt;/li&gt;
&lt;/ul&gt;</content><category term="build-CI-CD-devops"/><category term="cloud"/><category term="packer"/><category term="build"/><category term="ami"/><category term="chef"/></entry><entry><title>nginx with Docker</title><link href="https://blog.john-pfeiffer.com/nginx-with-docker/" rel="alternate"/><published>2015-06-13T08:00:00-07:00</published><updated>2015-06-13T08:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2015-06-13:/nginx-with-docker/</id><summary type="html">
&lt;h2 id="nginx-overview"&gt;nginx overview&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://nginx.org/en/"&gt;http://nginx.org/en/&lt;/a&gt; is one of the most popular performant web servers in the world (and it's pretty handy as a reverse proxy or load balancer too!). &lt;a href="http://nginx.org/en/docs/http/load_balancing.html"&gt;http://nginx.org/en/docs/http/load_balancing.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nginx/nginx"&gt;https://github.com/nginx/nginx&lt;/a&gt; is written in c (very performant but …&lt;/p&gt;</summary><content type="html">
&lt;h2 id="nginx-overview"&gt;nginx overview&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://nginx.org/en/"&gt;http://nginx.org/en/&lt;/a&gt; is one of the most popular performant web servers in the world (and it's pretty handy as a reverse proxy or load balancer too!). &lt;a href="http://nginx.org/en/docs/http/load_balancing.html"&gt;http://nginx.org/en/docs/http/load_balancing.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nginx/nginx"&gt;https://github.com/nginx/nginx&lt;/a&gt; is written in c (very performant but often needs to be compiled , especially with any of the extra 3rd party modules &lt;a href="https://www.nginx.com/resources/wiki/modules/"&gt;https://www.nginx.com/resources/wiki/modules/&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;A recent update means "...optionally load separate shared object files at runtime as modules" 
&lt;a href="https://www.nginx.com/blog/dynamic-modules-nginx-1-9-11/"&gt;https://www.nginx.com/blog/dynamic-modules-nginx-1-9-11/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="docker-pull-nginx"&gt;docker pull nginx&lt;/h2&gt;
&lt;p&gt;With Docker most of the time is spent in preparation, configuration, and testing.  The advantage is that less time is wasted on compiling, packaging, etc. (for all those still eeking out another 1% in efficiency via esoteric flags and bundling all sorts of custom modules - good luck!)&lt;/p&gt;
&lt;p&gt;One quick way to attempt to leverage nginx as a front end for your projects is using containers with Docker &lt;a href="https://hub.docker.com/_/nginx/"&gt;https://hub.docker.com/_/nginx/&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo su
docker pull nginx:alpine
docker images
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This will grab the latest image based on the very small alpine linux &lt;a href="https://en.wikipedia.org/wiki/Alpine_Linux"&gt;https://en.wikipedia.org/wiki/Alpine_Linux&lt;/a&gt;, around 13 MB vs 191 MB for the traditional nginx:latest which is based on debian jessie &lt;a href="https://en.wikipedia.org/wiki/Debian"&gt;https://en.wikipedia.org/wiki/Debian&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://hub.docker.com/r/library/nginx/tags/"&gt;https://hub.docker.com/r/library/nginx/tags/&lt;/a&gt; contains what other versions of nginx are provided by the vendor as Docker Images, the default build/image has quite a few modules &lt;a href="http://nginx.org/en/docs/"&gt;http://nginx.org/en/docs/&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;all future references to docker commands will assume you are root or typing sudo first&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="docker-nginx-interactive-container"&gt;docker nginx interactive container&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run --rm --publish 127.0.0.1:80:80 nginx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;starts an ephemeral container that binds the container port 80 to the local Host port 80 (binding to 127.0.0.1 prevents any other access except from the Host) , note by not explicitly sharing port 443 it is not connected/available&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                 PORTS                           NAMES
3fccf15a3c24        nginx               "nginx -g 'daemon off"   3 seconds ago       Up 2 seconds        127.0.0.1:80-&amp;gt;80/tcp, 443/tcp   pensive_elion

netstat -antp
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:80            0.0.0.0:*               LISTEN      5826/docker-proxy


curl 127.0.0.1:80
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;h1&amp;gt;Welcome to nginx!&amp;lt;/h1&amp;gt;&lt;/code&gt; is part of the default nginx home page, (success)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You will notice the access logs are being output to the console (where the docker container is running).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;192.168.1.100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;01&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Jan&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;1970&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;59&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mf"&gt;0000&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"GET / HTTP/1.1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;200&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;612&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"curl/7.38.0"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Control + C&lt;/code&gt; will terminate the container&lt;/p&gt;
&lt;h2 id="running-nginx-manually-in-the-interactive-docker-container"&gt;running nginx manually in the interactive docker container&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run --rm -i -t --publish 127.0.0.1:80:80 nginx /bin/bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Starting as root inside the container &lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="mi"&gt;@557&lt;/span&gt;&lt;span class="n"&gt;ac197e2c1&lt;/span&gt;&lt;span class="o"&gt;:/&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sbin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="nginx-version-and-modules"&gt;nginx version and modules&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="mi"&gt;@557&lt;/span&gt;&lt;span class="n"&gt;ac197e2c1&lt;/span&gt;&lt;span class="o"&gt;:/&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;1.9.7&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;built&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gcc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.9.2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Debian&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.9.2&lt;/span&gt;&lt;span class="mi"&gt;-10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;built&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OpenSSL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0.1&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Jan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2015&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;TLS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SNI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;support&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;sbin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sbin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;client_temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;proxy_temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fastcgi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;fastcgi_temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;uwsgi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;uwsgi_temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;scgi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;scgi_temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_ssl_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_realip_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_addition_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_sub_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_dav_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_flv_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_mp4_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_gunzip_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_gzip_static_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_random_index_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_secure_link_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_stub_status_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_auth_request_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stream_ssl_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mail_ssl_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aio&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;http_v2_module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;O2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fstack&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;protector&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;strong&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Wformat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Werror&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Wp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;D_FORTIFY_SOURCE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ld&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Wl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;relro&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Wl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;needed&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ipv6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="test-your-config-file"&gt;Test your config file&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;nginx -t -c /etc/nginx/nginx.conf -g "daemon off;"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="run-nginx-directly"&gt;run nginx directly&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/usr/sbin/nginx -c /etc/nginx/nginx.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;-g "pid /var/run/nginx.pid; worker_processes 2;" from &lt;a href="https://www.nginx.com/resources/wiki/start/topics/tutorials/commandline/"&gt;https://www.nginx.com/resources/wiki/start/topics/tutorials/commandline/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="nginxconf"&gt;nginx.conf&lt;/h2&gt;
&lt;p&gt;While there are quite a few ways to configure nginx one choice to make with Docker is to either&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;docker run --name some-nginx -v /some/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;run docker as a daemon with a specified Container Name and override the container nginx.conf file with /some/nginx.conf from the hose&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use a Dockerfile (based on the upstream nginx Docker image) to copy your own configuration file and other custom bits in and build your own custom Docker image (a highly recommended way of not being completely dependent on an upstream provider - especially if you push your Docker Image to your own private registry afterwards)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;e.g. &lt;a href="https://hub.docker.com/r/jwilder/nginx-proxy/~/dockerfile/"&gt;https://hub.docker.com/r/jwilder/nginx-proxy/~/dockerfile/&lt;/a&gt; which also has "Foreman in Go lang" and makes use of expecting the Host to provide the SSL certificates&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nginx.com/resources/admin-guide/nginx-web-server/"&gt;https://www.nginx.com/resources/admin-guide/nginx-web-server/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nginx.com/resources/wiki/start/topics/examples/full/"&gt;https://www.nginx.com/resources/wiki/start/topics/examples/full/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Quickly testing the nginx configuration file using an ephemeral docker container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;--publish&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;127&lt;/span&gt;.0.0.1:80:80&lt;span class="w"&gt; &lt;/span&gt;nginx&lt;span class="w"&gt; &lt;/span&gt;/bin/bash&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nginx -t -c /etc/nginx/nginx.conf"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;nginx:&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;configuration&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;/etc/nginx/nginx.conf&lt;span class="w"&gt; &lt;/span&gt;syntax&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;ok
&lt;span class="w"&gt;    &lt;/span&gt;nginx:&lt;span class="w"&gt; &lt;/span&gt;configuration&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;/etc/nginx/nginx.conf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;successful
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="build-your-own-nginx-dockerfile"&gt;Build your own nginx Dockerfile&lt;/h2&gt;
&lt;p&gt;If you require 3rd party modules then you will have to build nginx from source, e.g.
&lt;a href="https://github.com/openresty/headers-more-nginx-module#installation"&gt;https://github.com/openresty/headers-more-nginx-module#installation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That being the case you'll probably want to have a build process with 2 Docker files:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;the first Dockerfile will contain build-essentials, gcc, make, etc. so that you can build the binary from source (with any 3rd party modules) &lt;a href="http://nginx.org/en/docs/configure.html"&gt;http://nginx.org/en/docs/configure.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The second docker image would be your "production container" where you copy in the custom nginx binary, install the dependencies (i.e. openssl), and setup the default config.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="converting-an-existing-nginx-3rd-party-module-to-a-dynamic-module"&gt;Converting an existing nginx 3rd party module to a dynamic module&lt;/h3&gt;
&lt;p&gt;Since NGINX 1.9.11 supports dynamic modules and attempts to maintain API compatibility then it is possible to sometimes convert a module into a dynamic module (i.e. build the shared library object)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;./configure --add-dynamic-module=/opt/source/ngx_my_module/
make -f objs/Makefile modules
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Look for .so files in the objs directory after compilation or the modules subdirectory during installation&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://www.nginx.com/resources/wiki/extending/converting/"&gt;https://www.nginx.com/resources/wiki/extending/converting/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="configuring-nginx"&gt;Configuring nginx&lt;/h2&gt;
&lt;p&gt;There are entire books about how to configure nginx so I will just jot down some basics for myself.&lt;/p&gt;
&lt;h3 id="etcnginxnginxconf"&gt;/etc/nginx/nginx.conf&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;worker_processes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;worker_connections&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# include /etc/nginx/conf.d/*.conf;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Run the service as the www-data user and define 4 worker processes&lt;/li&gt;
&lt;li&gt;define an HTTP server that listens on port 80 by default&lt;/li&gt;
&lt;li&gt;the root location will return the contents of /var/www&lt;/li&gt;
&lt;li&gt;a best practice is to use multiple configuration files in the conf.d directory (as a really long complex configuration in a single is difficult to maintain)&lt;/li&gt;
&lt;li&gt;BUT we must comment it out as in the installation there can be a default.conf that overrides our nginx.conf&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo docker run -it --rm --publish 0.0.0.0:80:80 --volume /tmp/nginx.conf:/etc/nginx/nginx.conf:ro  nginx:alpine /bin/sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;debug the image interactively by overriding the default CMD with a shell (remember that Alpine is limited so &lt;code&gt;apk update&lt;/code&gt; and &lt;code&gt;apk add SOMENAME&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo docker run --rm --publish 0.0.0.0:80:80 --volume /tmp/nginx.conf:/etc/nginx/nginx.conf:ro  nginx:alpine /bin/sh -c "nginx -t -c /etc/nginx/nginx.conf"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;alternative method to just test your configuration file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;assumes you have a config file and some index file defined&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;echo&lt;span class="w"&gt; &lt;/span&gt;"&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&lt;/span&gt;hi&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;"&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/var/www/index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That's it, now you have nginx serving static files! (curl localhost OR use a browser and visit localhost or http://hostfqdn)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;of course /tmp is an insecure location so please store production nginx configuration files and web content from a secure directory in the docker host filesystem&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://nginx.org/en/docs/beginners_guide.html"&gt;http://nginx.org/en/docs/beginners_guide.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://nginx.org/en/docs/ngx_core_module.html#worker_processes"&gt;http://nginx.org/en/docs/ngx_core_module.html#worker_processes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="nginx-with-ssl"&gt;nginx with ssl&lt;/h3&gt;
&lt;p&gt;It is hard to imagine running a production (or even test or dev server that should mirror production) without SSL since all traffic could be intercepted or hijacked.  It takes a little more work but clearly it is an important step in running a service that others will use.&lt;/p&gt;
&lt;h4 id="etcnginxnginxconf_1"&gt;/etc/nginx/nginx.conf&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nobody&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;worker_processes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;worker_connections&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ssl_certificate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ssl_certificate_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="openssl-self-signed-certificates"&gt;openssl self signed certificates&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl&lt;span class="w"&gt; &lt;/span&gt;req&lt;span class="w"&gt; &lt;/span&gt;-subj&lt;span class="w"&gt; &lt;/span&gt;'/CN=example.com/O=My&lt;span class="w"&gt; &lt;/span&gt;Company&lt;span class="w"&gt; &lt;/span&gt;Name&lt;span class="w"&gt; &lt;/span&gt;LTD./C=US'&lt;span class="w"&gt; &lt;/span&gt;-new&lt;span class="w"&gt; &lt;/span&gt;-newkey&lt;span class="w"&gt; &lt;/span&gt;rsa:2048&lt;span class="w"&gt; &lt;/span&gt;-days&lt;span class="w"&gt; &lt;/span&gt;365&lt;span class="w"&gt; &lt;/span&gt;-nodes&lt;span class="w"&gt; &lt;/span&gt;-x509&lt;span class="w"&gt; &lt;/span&gt;-keyout&lt;span class="w"&gt; &lt;/span&gt;/tmp/server.key&lt;span class="w"&gt; &lt;/span&gt;-out&lt;span class="w"&gt; &lt;/span&gt;/tmp/server.crt

mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/tmp/www

echo&lt;span class="w"&gt; &lt;/span&gt;"&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&lt;/span&gt;hi&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;"&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp/www/index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="dockerized-nginx-with-ssl"&gt;dockerized nginx with ssl&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;alpine&lt;/span&gt;

&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;insecure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;
&lt;span class="n"&gt;firefox&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://nginx.org/en/docs/http/configuring_https_servers.html"&gt;http://nginx.org/en/docs/http/configuring_https_servers.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="nginx-with-ssl-and-http2"&gt;nginx with ssl and http/2&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/HTTP/2"&gt;https://en.wikipedia.org/wiki/HTTP/2&lt;/a&gt; is a new and more capable and performant standard for the venerable HTTP protocol.  It has widespread vendor support so you can use most modern servers (e.g. nginx) and most modern browsers (e.g. chrome) and get the benefits immediately. (With of course all sorts of fallbacks for legacy clients)&lt;/p&gt;
&lt;h4 id="etcnginxnginxconf_2"&gt;/etc/nginx/nginx.conf&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nobody&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;worker_processes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;worker_connections&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;301&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//$&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;default_server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ssl_certificate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ssl_certificate_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Assuming the previous nginx with ssl steps of creating a certificate and content, be aware that &lt;strong&gt;browsers permanently CACHE the 301 redirect&lt;/strong&gt; so use Private Browsing mode otherwise you will never see a different result for localhost =[&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="verifying-http2"&gt;Verifying HTTP/2&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;curl&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl&lt;span class="w"&gt; &lt;/span&gt;localhost

&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;&lt;/span&gt;301&lt;span class="w"&gt; &lt;/span&gt;Moved&lt;span class="w"&gt; &lt;/span&gt;Permanently&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;bgcolor=&lt;/span&gt;&lt;span class="s"&gt;"white"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;center&amp;gt;&amp;lt;h1&amp;gt;&lt;/span&gt;301&lt;span class="w"&gt; &lt;/span&gt;Moved&lt;span class="w"&gt; &lt;/span&gt;Permanently&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&amp;lt;/center&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;hr&amp;gt;&amp;lt;center&amp;gt;&lt;/span&gt;nginx/1.9.12&lt;span class="nt"&gt;&amp;lt;/center&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;


curl&lt;span class="w"&gt; &lt;/span&gt;--insecure&lt;span class="w"&gt; &lt;/span&gt;--location&lt;span class="w"&gt; &lt;/span&gt;localhost

&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&lt;/span&gt;hi&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;openssl s_client&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl s_client -connect localhost:443 -nextprotoneg ''

CONNECTED(00000003)
Protocols advertised by server: h2, http/1.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;chrome browser&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Browse directly to https://localhost/ with the chrome extension installed: &lt;a href="https://chrome.google.com/webstore/detail/http2-and-spdy-indicator/mpbpobfflnpcgagjijhmgnchggcjblin/related?hl=en"&gt;https://chrome.google.com/webstore/detail/http2-and-spdy-indicator/mpbpobfflnpcgagjijhmgnchggcjblin/related?hl=en&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The blue lightning symbol on the far far right (next to the "hamburger") indicates HTTP/2 is working.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;browsers permanently CACHE the 301 redirect&lt;/strong&gt; so connecting to http://localhost will forever see a different result for localhost =[&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Browse directly to https://localhost/  (If using a self signed certificate accept any warnings about insecure SSL)&lt;/li&gt;
&lt;li&gt;Chrome/Chromium Settings -&amp;gt; More tools -&amp;gt; Developer tools (aka Control + Shift + I)&lt;/li&gt;
&lt;li&gt;Click on the Network section&lt;/li&gt;
&lt;li&gt;Control + Shift + F5 to reload (or click on the arrow circling up)&lt;/li&gt;
&lt;li&gt;Right click on the Name colum in the result so that you can add results for column heading, "Protocol"&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Control + Shift + F5 to reload (or click on the arrow circling up)&lt;/p&gt;
&lt;p&gt;Protocol: h2&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.nginx.com/blog/nginx-1-9-5/"&gt;https://www.nginx.com/blog/nginx-1-9-5/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.cloudflare.com/tools-for-debugging-testing-and-using-http-2/"&gt;https://blog.cloudflare.com/tools-for-debugging-testing-and-using-http-2/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tech.finn.no/2015/09/25/setup-nginx-with-http2-for-local-development/"&gt;http://tech.finn.no/2015/09/25/setup-nginx-with-http2-for-local-development/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="nginx-with-php-fpm"&gt;nginx with php-fpm&lt;/h2&gt;
&lt;h3 id="investigate-php-fpm-in-docker"&gt;Investigate php-fpm in Docker&lt;/h3&gt;
&lt;p&gt;This hack is fun but proves unnecessary when using Docker Compose later...&lt;/p&gt;
&lt;p&gt;:::bash
    docker pull php:5.5-fpm-alpine
    ifconfig | grep Bc
    docker run -it --rm --publish 0.0.0.0:9000:9000 php:5.5-fpm-alpine /bin/sh
    route -n
    php-fpm --version
    sed -i 's/listen = 127.0.0.1:9000/listen = 0.0.0.0:9000/g' /usr/local/etc/php-fpm.d/www.conf
    php-fpm --fpm-config /usr/local/etc/php-fpm.conf&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;ifconfig and route -n are to discover the docker host IP Address via the default gateway 172.17.0.1&lt;/li&gt;
&lt;li&gt;contains include=etc/php-fpm.d/*.conf&lt;/li&gt;
&lt;li&gt;/usr/local/etc/php-fpm.d/www.conf&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/php/"&gt;https://hub.docker.com/_/php/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://php.net/manual/en/install.fpm.configuration.php"&gt;http://php.net/manual/en/install.fpm.configuration.php&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="configure-nginx"&gt;Configure nginx&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;vi /etc/nginx/nginx.conf&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;worker_processes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;worker_connections&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;try_files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;try_files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;fastcgi_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;# depends on Docker linking or hostfile for fpm to resolve&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;fastcgi_pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;fastcgi_param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SCRIPT_FILENAME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;document_root&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;fastcgi_script_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;# https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-5385&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;fastcgi_param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HTTP_PROXY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fastcgi_params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;a basic nginx config that forwards .php requests to fpm on port 9000&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;
&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This manual command proves unnecessary with Docker Compose but can be useful to debug (i.e. run the php-fpm container first, then run this one since it depends on fpm)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="some-test-content"&gt;Some Test Content&lt;/h4&gt;
&lt;p&gt;Create the following files to test the various cases...&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vi /tmp/www/html/foo.html&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&lt;/span&gt;hi&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;vi /tmp/www/html/index.php&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;hello&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;vi /tmp/www/html/bar.php&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="docker-composeyml"&gt;docker-compose.yml&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# https://docs.docker.com/compose/compose-file/&lt;/span&gt;

&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;alpine&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"80:80"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fpm&lt;/span&gt;

&lt;span class="n"&gt;fpm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;5.5&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fpm&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;alpine&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9000:9000"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;

&lt;span class="c1"&gt;# http://stackoverflow.com/questions/29905953/how-to-correctly-link-php-fpm-and-nginx-docker-containers-together&lt;/span&gt;
&lt;span class="c1"&gt;# http://stackoverflow.com/questions/35388590/issue-with-docker-compose-container-command-not-found&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="docker-compose-up-to-start-nginx-and-php-fpm"&gt;docker-compose up to start nginx and php-fpm&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker-compose&lt;span class="w"&gt; &lt;/span&gt;up
docker-compose&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-f
curl&lt;span class="w"&gt; &lt;/span&gt;localhost:80/foo.html
curl&lt;span class="w"&gt; &lt;/span&gt;localhost:80/bar.php
curl&lt;span class="w"&gt; &lt;/span&gt;localhost:80/index.php
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Another tiny step forward in tying together a lot of moving pieces =]&lt;/p&gt;</content><category term="virtualization"/><category term="docker"/><category term="nginx"/></entry><entry><title>Meeting Bjarne Stroustrup, creator of C plus plus, in the Atlassian Dev Den</title><link href="https://blog.john-pfeiffer.com/meeting-bjarne-stroustrup-creator-of-c-plus-plus-in-the-atlassian-dev-den/" rel="alternate"/><published>2015-06-03T20:30:00-07:00</published><updated>2015-06-03T20:30:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2015-06-03:/meeting-bjarne-stroustrup-creator-of-c-plus-plus-in-the-atlassian-dev-den/</id><summary type="html">
&lt;h3 id="bjarne-stroustrup-visits-atlassian"&gt;Bjarne Stroustrup visits Atlassian&lt;/h3&gt;
&lt;p&gt;One of the things I looked forward to when joining Atlassian was being a part of a culture that celebrated software development.&lt;/p&gt;
&lt;p&gt;Today I had the opportunity to meet Bjarne Stroustrup &lt;a href="http://www.stroustrup.com/"&gt;http://www.stroustrup.com/&lt;/a&gt;, creator of C++. (yey)&lt;/p&gt;
&lt;p&gt;&lt;img alt="Photo of John and Bjarne" src="../images/2015-06-03--16.02.34-john-with-bjarne.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;To put this in perspective, C++ is …&lt;/p&gt;</summary><content type="html">
&lt;h3 id="bjarne-stroustrup-visits-atlassian"&gt;Bjarne Stroustrup visits Atlassian&lt;/h3&gt;
&lt;p&gt;One of the things I looked forward to when joining Atlassian was being a part of a culture that celebrated software development.&lt;/p&gt;
&lt;p&gt;Today I had the opportunity to meet Bjarne Stroustrup &lt;a href="http://www.stroustrup.com/"&gt;http://www.stroustrup.com/&lt;/a&gt;, creator of C++. (yey)&lt;/p&gt;
&lt;p&gt;&lt;img alt="Photo of John and Bjarne" src="../images/2015-06-03--16.02.34-john-with-bjarne.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;To put this in perspective, C++ is one of the most popular programming languages of all time and is essentially like meeting the inventor of the hammer. (mindblown)&lt;/p&gt;
&lt;p&gt;To summarize, he was one of the most intelligent, mindful, humble, and quietly passionate people I have ever met.  Truly an inspiration to any engineer.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I have taken a tiny amount of free license due to my faulty memory and for readability.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="getting-the-book-signed"&gt;Getting the book signed&lt;/h3&gt;
&lt;p&gt;I had dug out my old The C++ Programming Language (3rd edition from 1997) which filled me with nostalgia of how amazing it was to read a text book that was just full of succinct coding amazingness.  So many textbooks were full of fluff, confusion, and downright goofiness but that book opened my mind to the difference it makes to have the original inventor writing it (Kernighan and Ritchie ;)&lt;/p&gt;
&lt;p&gt;Lucky me, I was there about 15 minutes early and while they got the Audio/Video set up I bravely politely asked him to sign it. (yes, success!)&lt;/p&gt;
&lt;p&gt;There was one other co worker there who was helping him prepare (and apparently also a huge fan, I took her picture with him) and I managed to ask some small talk questions (go me!)&lt;/p&gt;
&lt;p&gt;Bjarne seemed distant at first and his driver/handler (former Chief Technology Officer somewhere important) was maybe a bit apprehensive but I suspect the fan-ness of it all relaxed a bit after we proved we weren't pyschos...&lt;/p&gt;
&lt;p&gt;"This book was really amazing."  He responded, "Well this one is a bit old, you should use a newer edition".&lt;/p&gt;
&lt;p&gt;I quickly replied, "I don't actually use this in my day to day work so I keep it at home. It's a bit heavy to carry around all the time, I read a lot of things on my phone (easier to get the latest version of the books)."&lt;/p&gt;
&lt;p&gt;I must have hit a topic of interest for him as he came over and I had flipped open the book to some sample code.&lt;/p&gt;
&lt;p&gt;He ruminated, "Well, actually, I prefer this kind of reading with a book.  The formatting and print... you have to work hard to make sure the examples don't get split across pages.  And you have two pages." (Gesturing with his hands the extra width)&lt;/p&gt;
&lt;p&gt;I agreed with him, "Yes, it is certainly a lot easier to read the diagrams and code examples in a physical book.  The phone screen is really small.  I guess the book is a perfected technology that's been around 1000 years."&lt;/p&gt;
&lt;p&gt;"You know Atlassian isn't really a C++ shop, more Java with some Javascript... and Python products teams here in SF.  Actually it's even a really argumentative culture sometimes around technology, though with you here I'm sure they'll all be quiet and respectful."&lt;/p&gt;
&lt;p&gt;He actually smiled at that, "Back in AT&amp;amp;T we would ask: was there blood on the floor? That was how serious the discussions got.  Not really any blood on the floor, just an expression."&lt;/p&gt;
&lt;p&gt;"What do you enjoy most from your travels/touring?"&lt;/p&gt;
&lt;p&gt;Such a wise response: "Meeting the people, learning about what they're thinking and what new ideas they might have".&lt;/p&gt;
&lt;p&gt;Looking around the mostly empty room with 1 minute before the event starts, "Well I don't know where everyone else is, I'm sure there's lots of interest but the SF office is the Marketing and Business headquarters with only a few Development/Product teams".  He responded, "Yesterday Facebook was standing room only."&lt;/p&gt;
&lt;p&gt;I looked over at the monitors, "Well I'm sure there'll be attendance from the other offices too".  &lt;/p&gt;
&lt;p&gt;"What other locations does Atlassian have?" &lt;/p&gt;
&lt;p&gt;"Austin, and of course Sydney has 800 Java Developers, and Poland, and Vietnam, it's a pretty globally diverse company".&lt;/p&gt;
&lt;p&gt;I also warned him about the air conditioning intermittently coming on and being loud and often too cold.  "I was baking hot yesterday so I guess if it's cold in here today I guess it will average out". (With his curious funny little smile)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And with the microphone on and an international audience he was introduced and here were the questions and answers...&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="current-challenges-for-programming-languages"&gt;Current challenges for programming languages&lt;/h3&gt;
&lt;p&gt;Since hardware improvements are through parallelization (unless quantum entanglements gets solved soon) developers must adapt, but concurrency is hard.  (And no global variables, right?)&lt;/p&gt;
&lt;p&gt;"Functional programming has good ideas but I did not want everything to be a recursive function"&lt;/p&gt;
&lt;p&gt;Distributed systems are a new perspective problem: for a fair trade, an exchange in New York offering the same price to Sydney and San Francisco becomes difficult (the speed of light is still a limit).&lt;/p&gt;
&lt;p&gt;Anyone heard of the leap second?  There are at least 3 ways it is being dealt with:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hold on for a really long time and then jump forward all at once.&lt;/li&gt;
&lt;li&gt;Jump forward immediately.&lt;/li&gt;
&lt;li&gt;Break the leap second into a lot of intervals and merge it in.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But if it's a distributed system how can everyone agree?  And that's just one example.&lt;/p&gt;
&lt;h3 id="trends-in-programming"&gt;Trends in programming?&lt;/h3&gt;
&lt;p&gt;Silicon Valley is an echo chamber but you probably know more than I do.  &lt;/p&gt;
&lt;p&gt;Mobile is very hot but it is a very small screen, and not everything (like a textbook) can be read on a small phone screen or even a tablet screen.&lt;/p&gt;
&lt;h3 id="a-second-favorite-language"&gt;A second favorite language?&lt;/h3&gt;
&lt;p&gt;Bjarne was incredibly diplomatic about pointing out that any language he said he preferred might be tweeted and start a "language war";  language comparisons are useless without the context of the constraints of the problem domain.&lt;/p&gt;
&lt;p&gt;For instance he had written C++ to be a performant language with the assumption of unix shell as the "other language".&lt;/p&gt;
&lt;p&gt;But what about Rust "Zero Cost Abstractions"? "It's not good to pay for what you don't use."&lt;/p&gt;
&lt;p&gt;He admitted that since he coined the phrase "Abstraction without overhead" that indeed he admired the goal of Rust (getting close to the hardware for maximum performance) but hadn't been following their recent developments.  For garbage collection... you shouldn't litter.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blog.rust-lang.org/2015/05/11/traits.html"&gt;http://blog.rust-lang.org/2015/05/11/traits.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Languages are about solving problems so I am very familiar with the strengths and weaknesses of C++ and I know how to make it do quite a lot.  But if I use another language then I am an amateur, not familiar with the idioms, and perhaps I will not use it so well.&lt;/p&gt;
&lt;h3 id="any-features-you-regret-putting-in-or-leaving-out-of-c"&gt;Any features you regret putting in or leaving out of C++?&lt;/h3&gt;
&lt;p&gt;No, everything in there has a use and ... they have to stay or it would upset thousands of people.    I wish it had better syntax, starting from C, well even they realized it was a mistake but it was already done so what are you going to do.&lt;/p&gt;
&lt;p&gt;Anything you put in a language, especially a mistake, will stay with you.  Maybe you can put something off to release a product and know you will get back to it in an iteration or two.  But make a mistake with a language and you live with it for a couple of decades.&lt;/p&gt;
&lt;p&gt;I do wish I had gotten some things right earlier.  Templates.  They solved the problem really well, general enough to do more than I imagined (that's the criteria for sucess).  Nobody else at the time knew how to solve it but I wish the interface was better.  Now, with the upcoming "Concepts", it will be better, but I wish I had done it right the first time.&lt;/p&gt;
&lt;p&gt;You have to understand, at the time of creation C was constrained to 64K, and C++ to 256K, so there are simply things you cannot do then.&lt;/p&gt;
&lt;h3 id="a-complicated-question-about-the-n4047-modules-proposal"&gt;A complicated question about the N4047 modules proposal&lt;/h3&gt;
&lt;p&gt;It was a good proposal.  Isolating code from other portions to make compilation faster.&lt;/p&gt;
&lt;p&gt;C++ is maintained by people who actually pay 1280 to be on the committee... they don't make any money by doing it. Daveed Vandevoorde had to continue working and doing this proposal was something on the side.&lt;/p&gt;
&lt;p&gt;If it is not in my top 20 then it's not an important problem.  So a lot of good proposals do not make my top 20.&lt;/p&gt;
&lt;p&gt;More recently teams in Microsoft, Google, GCC, and Clang are looking at this and some will even have implementations before it is released.&lt;/p&gt;
&lt;p&gt;It should be including in release 17 and you may see a compilation speedup between %50 and 50x , maybe more usually 4x or 8x faster.&lt;/p&gt;
&lt;p&gt;Language features are complicated as you have to coordinate between all of the major tool chains (you certainly can't have one of them do it one way and another differently).&lt;/p&gt;
&lt;h3 id="how-many-languages"&gt;How many languages?&lt;/h3&gt;
&lt;p&gt;When asked about whether a developer should focus on one language or learn many Bjarne had a very nuanced answer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When he started programming it was not impossible to learn 25 languages.  It is very valuable to learn the different ways of thinking and solving problems by programming in a different language. Now though, languages are more complex with a lot of libraries, idioms, and toolchain to learn. &lt;/p&gt;
&lt;p&gt;So learning two languages, like the example before of 1 performant and 1 more general, would not be enough since that would not provide any comparison of why. Maybe 4, so that you could compare two general and two performant languages and understand how/why they did things differently.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="my-question"&gt;My Question&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;I decided to mull over and try and ask a live question (with an audience this time).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;"Given your comment about tooling before, how valuable do you find developer tools to a programming language (like an IDE)?  Also, what are any major gaps you see in current tools?"&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;"Good question.  I don't really use tools that much since I work on smaller examples that might need to be ported in a few places.  On larger projects and code bases tooling is very useful as you can't keep all of it in your head anymore.  I'm maybe not the best person to ask, you probably work on large code more than I.  But just being able to click on a macro or identifier and jump to it in the code is very useful."&lt;/p&gt;
&lt;p&gt;"I don't use a debugger very often.  Other people might but I don't.  If you are using a debugger it's maybe a bad sign about your code. But I would like to see a debugger that could follow concurrency.  That would really help."&lt;/p&gt;
&lt;p&gt;"Tests are really important, so tools that make it easy to run tests, and to write tests quickly, that would be very useful."&lt;/p&gt;
&lt;h3 id="what-is-a-professional"&gt;What is a professional?&lt;/h3&gt;
&lt;p&gt;If someone, your employer or your manager, asks you to do something that is shabby you should just resign.  It doesn't happen very often but there are ethics.&lt;/p&gt;
&lt;p&gt;I wish the Universities, that teach "Computer Science" and not programming, would spend some time on caching or real world coding and problems.  They graduate being able to describe the Halting Problem but with magic constants throughout their code.&lt;/p&gt;
&lt;p&gt;It would be nice if there was a more universal way to explain/teach being "professional" in software.&lt;/p&gt;
&lt;h3 id="c-in-education"&gt;C++ in Education&lt;/h3&gt;
&lt;p&gt;Bjarne was asked and clearly had some strong feelings on the subject of why C++ wasn't taught in universities.&lt;/p&gt;
&lt;p&gt;"Perhaps it's because C++ doesn't have a marketing budget" he quipped.  More seriously he went on to point out "he had very successfully taught college freshman programming in C++ so it's quite possible.  There isn't enough time to teach all of C first so you don't start with that: for instance pointers are chapter 17, not chapter 2."&lt;/p&gt;
&lt;h3 id="more-info"&gt;More info&lt;/h3&gt;
&lt;p&gt;An interesting interview he gave for comparison: &lt;a href="http://www.stroustrup.com/CVu263interview.pdf"&gt;http://www.stroustrup.com/CVu263interview.pdf&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="c++"/><category term="cpp"/><category term="atlassian"/></entry><entry><title>Pragmatic testing, from Makefile to CI with Docker</title><link href="https://blog.john-pfeiffer.com/pragmatic-testing-from-makefile-to-ci-with-docker/" rel="alternate"/><published>2015-05-25T10:00:00-07:00</published><updated>2015-05-25T10:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2015-05-25:/pragmatic-testing-from-makefile-to-ci-with-docker/</id><summary type="html">
&lt;p&gt;A colleague recently suggested "Hey, why don't you run those tests from outside of the target server under test?"&lt;/p&gt;
&lt;p&gt;And I thought to myself, "Hmm.... why are we doing it that way?  Was I just dumb when I did this the first time?&lt;/p&gt;
&lt;p&gt;The answer is the journey of successfully …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;A colleague recently suggested "Hey, why don't you run those tests from outside of the target server under test?"&lt;/p&gt;
&lt;p&gt;And I thought to myself, "Hmm.... why are we doing it that way?  Was I just dumb when I did this the first time?&lt;/p&gt;
&lt;p&gt;The answer is the journey of successfully testing a successful product and the pragmatic choices made along the way.  I believe that engineering requires compromises because without achieving short term progress we would have never reached our bigger, long term goals.&lt;/p&gt;
&lt;h3 id="humble-beginnings-with-makefile"&gt;Humble beginnings with Makefile&lt;/h3&gt;
&lt;p&gt;Our initial project was to deliver an OVA (open virtualization archive) that packaged all of the services (and dependencies) into an easy to deploy and maintain virtual appliance.&lt;/p&gt;
&lt;p&gt;Will, as the project lead, had already spent some time setting up the build environment using vStudio and an Ubuntu ISO but one of our first issues after generating an OVA was to try and determine if it was "good".  Could it even be deployed?  Did it have the services installed correctly?&lt;/p&gt;
&lt;p&gt;I leveraged the Makefile that we were already using for the build to create a "test": deploy the latest OVA artifact and check that it was basically sound.&lt;/p&gt;
&lt;p&gt;This was a good start as, at the very least, we could quickly determine and bisect where we were producing bad builds.&lt;/p&gt;
&lt;h3 id="adding-some-tests-but-where"&gt;Adding some tests, but where?&lt;/h3&gt;
&lt;p&gt;Once the initial spike ("tracer bullet") was done I began adding in selenium python based tests to verify the Web UI interface.&lt;/p&gt;
&lt;p&gt;Since our Continuous Integration system was basically bash and Makefile (which was also our build system) I opted to run the tests from inside of each deployed Virtual Machine.&lt;/p&gt;
&lt;p&gt;This allowed for isolation of the test execution from the build process and for each test run.  &lt;/p&gt;
&lt;p&gt;While not ideal for product acceptance testing it provided a basic safety net that allowed us to know if a breaking change occurred upstream and was the first automated verification (followed by lots of manual testing) of the exact OVA we were shipping to Customers in the Beta.&lt;/p&gt;
&lt;h3 id="bamboo-continuous-integration-for-visibility-and-the-team"&gt;Bamboo Continuous Integration for visibility and the team&lt;/h3&gt;
&lt;p&gt;A further improvement was to improve visibility of the test results.  In parallel, after the team discussion during "HipCon" and with motivation from Don and Sam, I setup a private Bamboo installation in our VM area and helped get Integration tests setup for our upstream backend code.&lt;/p&gt;
&lt;p&gt;Once again I stuck with the tried and true "tests inside of the target virtual machine" pattern as the Bamboo server was only using "local agents" and I was concerned about trying to maintain a clean environment and the resources required as the number of test plans scaled.&lt;/p&gt;
&lt;p&gt;Additionally I migrated all of the previous test plans from the "build factory" into Bamboo which really helped with failed test visibility and tracking over time.&lt;/p&gt;
&lt;p&gt;The unit tests continued to run in a different SaaS version of Bamboo so I avoided scope creep and left things that were working alone.&lt;/p&gt;
&lt;h3 id="migrating-to-a-managed-service"&gt;Migrating to a managed service&lt;/h3&gt;
&lt;p&gt;One of the most asked questions whenever a new person joined was "Why are the tests spread across so many different servers/services"?  I had to answer that question so many times!  The answer basically boiled down to "the testing grew organically as different people in the team solved the problem they faced".  Unsatisfactory? "Be the change you seek" didn't seem to get anyone else to solve the problem for me ;)&lt;/p&gt;
&lt;p&gt;Using the opportunity presented by a service outage issue I pushed forward a plan I had to migrate all of the test plans into a newly provisioned Bamboo server run by Build Engineering. (awthanks)&lt;/p&gt;
&lt;p&gt;There was an awesome team effort by a lot of people to make that happen (made even more challenging by doing it when the source service was out). (awesome)&lt;/p&gt;
&lt;p&gt;This solved quite a few problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Unit, Integration, and Product Acceptance tests were all finally combined under one roof... one UI to rule them all!&lt;/li&gt;
&lt;li&gt;New people joining the team needing access to Bamboo: since I hadn't linked it to any directory/authentication I was adding users manually - we were finally able to leverage an Atlassian backend Directory system&lt;/li&gt;
&lt;li&gt;I had been managing upgrades and maintenance of the Bamboo software (not fun and not my core expertise)&lt;/li&gt;
&lt;li&gt;I began to worry about the resource consumption (since this was running on hardware that also provided for our Build Factory) - no longer a worry with lots of Remote Agents and Elastic Agents (all provided by Build Engineering)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Bonuses:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;More plugins and capabilities and knowledge from the Bamboo server and Build Engineering expertise&lt;/li&gt;
&lt;li&gt;Plan Templates to keep test plans in version control and macros for common functionality&lt;/li&gt;
&lt;li&gt;A successful spike of using Docker for unit testing&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With so much going on during the migration I avoided changing the testing paradigm, so tests continued to execute inside of each VM/EC2 Instance deployed. (shrug)&lt;/p&gt;
&lt;h3 id="adopting-docker-and-refactoring"&gt;Adopting Docker and refactoring&lt;/h3&gt;
&lt;p&gt;Remember that question at the beginning?  When something goes wrong, while it makes sense to separate "fixing it" from "improving it" I'm a big fan of taking advantage of having the hood open to go the extra mile and leave the campground cleaner =)&lt;/p&gt;
&lt;p&gt;So some of the selenium based tests were failing and it occurred to me that some of how we were changing our dependency infrastructure at the operating system level could be the cause.  After some unproductive poking around I tried to reproduce and isolate the issue by running the tests from outside of the VM.&lt;/p&gt;
&lt;p&gt;Aha! In that moment I realized that this was actually the desired (original intention, honest!) way to run the blackbox product acceptance tests.&lt;/p&gt;
&lt;p&gt;So I pulled up my sleeves and tried out the "hot fancy new silver bullet technology that solves every problem".&lt;/p&gt;
&lt;h4 id="why-docker"&gt;Why Docker?&lt;/h4&gt;
&lt;p&gt;Docker encourages design of modular, deterministic and defined, single purpose components that are easy to reuse and compose into larger services. &lt;/p&gt;
&lt;p&gt;Not only are (Docker) Containers fast, one of the biggest advantages of Containers is the ability to reduce complexity. Docker can turn an application/service, it's dependencies, and even the OS level requirements into a single blackbox package (that you can still inspect inside if you really want to).&lt;/p&gt;
&lt;p&gt;So I built a Docker Image containing python selenium and &lt;a href="http://phantomjs.org/"&gt;http://phantomjs.org/&lt;/a&gt; (a headless javascript based browser) and other dependencies.&lt;/p&gt;
&lt;p&gt;Sure enough I the tests passed when leveraging the previous Docker spike to run my new docker container. (success)&lt;/p&gt;
&lt;p&gt;Refactoring the bamboo plan (since it was leveraging Plan Templates and the Groovy DSL macros) didn't take too long and with other stakeholders PR/approval we're moving full speed ahead towards the "ideal" solution. (It only took about 2 years).&lt;/p&gt;
&lt;h3 id="dogfooding-the-whole-way"&gt;Dogfooding the whole way&lt;/h3&gt;
&lt;p&gt;Something I should mention that has been an invaluable companion throughout the course of building the product: a dogfooding server.&lt;/p&gt;
&lt;p&gt;"Eating our own dog food" is a wonderful way to experience the exact pain you are inflicting on your users.  From the very begininng Will setup and we maintained a dogfood server which received every beta upgrade (and a few upgrades that never reached the customers), amazingly it's still alive and full of data!&lt;/p&gt;
&lt;p&gt;Not only did I learn about bugs that would affect our oldest and most loyal users (who kept with us and kept upgrading), I also felt the User Experience pain of how long upgrades took, mysterious incomprehensible errors messages, and "partial upgrades".  All of these learnings, along with being Developer on Support and assisting on support tickets, kept me honest and humble and allowed me to improve the product just as much as any fancy testing automation framework.&lt;/p&gt;
&lt;h3 id="what-does-this-all-mean"&gt;What does this all mean?&lt;/h3&gt;
&lt;p&gt;It's easy to draw up how things should work according to best practice.  It's even easier if it's work that someone else has to do and there aren't any deadlines.&lt;/p&gt;
&lt;p&gt;Success comes in stages.  Overengineering and premature optimization cost way more in opportunity cost and thrown away work than doing things the "wrong way".&lt;/p&gt;
&lt;p&gt;This story could be massaged to fit a parable of "Lean and Agile" but it's really just common sense about transparently understanding the cost/value tradeof of the work, solving the current needs, and moving forward onto something better (by keeping informed of new solutions) when the opportunity shows up.&lt;/p&gt;</content><category term="build-CI-CD-devops"/><category term="testing"/><category term="selenium"/><category term="docker"/></entry><entry><title>HAProxy in Docker</title><link href="https://blog.john-pfeiffer.com/haproxy-in-docker/" rel="alternate"/><published>2015-05-21T20:00:00-07:00</published><updated>2015-05-21T20:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2015-05-21:/haproxy-in-docker/</id><summary type="html">
&lt;p&gt;Not only are Containers fast, one of the biggest advantages of Containers is the ability to reduce complexity.&lt;/p&gt;
&lt;p&gt;Docker can turn an application/service, it's dependencies, and even the OS level requirements into a single blackbox package (that you can still inspect inside if you really want to).&lt;/p&gt;
&lt;p&gt;One thing …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Not only are Containers fast, one of the biggest advantages of Containers is the ability to reduce complexity.&lt;/p&gt;
&lt;p&gt;Docker can turn an application/service, it's dependencies, and even the OS level requirements into a single blackbox package (that you can still inspect inside if you really want to).&lt;/p&gt;
&lt;p&gt;One thing I really like is less code.  Seriously.  Configuration over coding (whenever I don't need customization) means far less maintenance and bugs.&lt;/p&gt;
&lt;p&gt;Here's a trivial example of how I can leverage the HAProxy Docker image/container to load balance two web servers. (aka "reverse proxy" &lt;a href="http://en.wikipedia.org/wiki/Reverse_proxy"&gt;http://en.wikipedia.org/wiki/Reverse_proxy&lt;/a&gt;)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sites&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;haproxy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;
&lt;span class="n"&gt;BackendA&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;BackendB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;There are new problems that go along with the benefits of any new technology, see the complicated networking/port coordination&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo docker pull haproxy:1.5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="some-backend-web-servers"&gt;Some backend web servers&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;BackendA&lt;/span&gt;
&lt;span class="nx"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;BackendA&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txt&lt;/span&gt;
&lt;span class="nx"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;BackendA&lt;/span&gt;
&lt;span class="nx"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SimpleHTTPServer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;

&lt;span class="nx"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;BackendB&lt;/span&gt;
&lt;span class="nx"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;BackendB&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txt&lt;/span&gt;
&lt;span class="nx"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;BackendB&lt;/span&gt;
&lt;span class="nx"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SimpleHTTPServer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8001&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Clearly a trivial example (more likely two remote hosts in logical/geographic disparate areas if aiming for High Availability, or at least on different hosts to scale with more resources)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="optmydatahaproxycfg"&gt;/opt/mydata/haproxy.cfg&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;global
        debug

defaults
        log global
        mode    http
        timeout connect 5000
        timeout client 5000
        timeout server 5000

listen http_proxy :8443
        mode tcp
        balance roundrobin
        server srv1 docker:8000 check
        server srv2 docker:8001 check
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="start-haproxysh"&gt;start haproxy.sh&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;HOSTIP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;ip&lt;span class="w"&gt; &lt;/span&gt;addr&lt;span class="w"&gt; &lt;/span&gt;show&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;docker0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;global&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;awk&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cut&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;-f1&lt;span class="sb"&gt;`&lt;/span&gt;

sudo&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8443&lt;/span&gt;:8443&lt;span class="w"&gt; &lt;/span&gt;--add-host&lt;span class="o"&gt;=&lt;/span&gt;docker:&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOSTIP&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;-it&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;/opt/mydata/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;myhaproxy&lt;span class="w"&gt; &lt;/span&gt;haproxy:1.5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Now that the docker container's /etc/hosts file has the Host IP Address injected (with the name "docker") the haproxy config file probably makes more sense&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;running the container&lt;/li&gt;
&lt;li&gt;the host port 8443 mapped to the container port 8443&lt;/li&gt;
&lt;li&gt;injecting into the container /etc/hosts the Host IP Address as "docker"&lt;/li&gt;
&lt;li&gt;ephemeral container (automatic cleanup on termination)&lt;/li&gt;
&lt;li&gt;interactive&lt;/li&gt;
&lt;li&gt;tty&lt;/li&gt;
&lt;li&gt;readonly mapping of the /opt/mydata/haproxy.cfg file on the host to /usr/local/etc/haproxy/haproxy.cfg&lt;/li&gt;
&lt;li&gt;name the container myhaproxy (each container name must be unique)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;the container is using the haproxy version 1.5 Docker Image&lt;/p&gt;
&lt;p&gt;./start-haproxy.sh&lt;/p&gt;
&lt;p&gt;curl localhost:8443&lt;/p&gt;
&lt;p&gt;:::html
&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"&amp;gt;&lt;html&gt;
&lt;title&gt;Directory listing for /&lt;/title&gt;
&lt;body&gt;
&lt;h2&gt;Directory listing for /&lt;/h2&gt;
&lt;hr/&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="bar.txt"&gt;bar.txt&lt;/a&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;hr/&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;curl localhost:8443&lt;/p&gt;
&lt;p&gt;:::html
&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"&amp;gt;&lt;html&gt;
&lt;title&gt;Directory listing for /&lt;/title&gt;
&lt;body&gt;
&lt;h2&gt;Directory listing for /&lt;/h2&gt;
&lt;hr/&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="foo.txt"&gt;foo.txt&lt;/a&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;hr/&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="haproxy-stats"&gt;HAProxy Stats&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo docker rm -f myhaproxy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="optmydatahaproxycfg_1"&gt;/opt/mydata/haproxy.cfg&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;global
    debug

defaults
    log global
    mode    http
    timeout connect 5000
    timeout client 5000
    timeout server 5000

listen http_proxy :8443
    mode tcp
    balance roundrobin
    server srv1 docker:8000 check
    server srv2 docker:8001 check

&lt;span class="gh"&gt;#&lt;/span&gt; optional section to enable statistics for haproxy protected by basic auth (replace with your own user and password)
listen stats :1936
    stats enable
    stats uri /
    stats realm HAProxyStatistics
    stats auth user:password


./start-haproxy.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="nginx-forward-proxy"&gt;nginx forward proxy&lt;/h3&gt;
&lt;p&gt;client -&amp;gt; forward proxy (nginx) -&amp;gt; all other sites&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker pull nginx:alpine
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="nginxconf"&gt;nginx.conf&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;worker_processes&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;worker_connections&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nx"&gt;mime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;default_type&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;octet&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;sendfile&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;keepalive_timeout&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;gzip&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;resolver&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;8.8.8.8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;proxy_pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//$http_host$uri$is_args$args;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;error_page&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;502&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;ro&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mynginx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;bind to port 8080 on the host and run an ephemeral container based on the alpine linux with nginx image that uses the /tmp/nginx config&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;http_proxy=127.0.0.1:8080 curl example.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;set the linux operating system proxy environment just for this one curl command and see the dockerized nginx forward proxy log show: 172.17.0.1 - - [06/May/2016:22:37:22 +0000] "GET HTTP://example.org/ HTTP/1.1" 200 1270 "-" "curl/7.35.0"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Configure your browser (firefox) to use 127.0.0.1:8080 as the proxy for all protocols and watch the log statements fly by when you test http://example.com&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;NOTE: this does not support HTTPS &lt;a href="http://forum.nginx.org/read.php?2,15124,15256#msg-15256"&gt;http://forum.nginx.org/read.php?2,15124,15256#msg-15256&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;Attempting https://example.com will return "CONNECT example.com:443 HTTP/1.1" 400 173 "-" "-"  and the browser will show "Server not found"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;** Warning ** do not use &lt;em&gt;http_proxy=http://127.0.0.1:8080&lt;/em&gt; as that will fail =[&lt;/p&gt;
&lt;p&gt;You can permanently set the environment proxy with export http_proxy=127.0.0.1:8080&lt;/p&gt;
&lt;h3 id="haproxy-as-a-limited-outbound-proxy"&gt;HAProxy as a limited outbound proxy&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo docker pull haproxy:1.6-alpine
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now that you have the 10MB haproxy image...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note there is a limitation to haproxy in that it always assumes a syslog facility (no direct logging to stdout or files)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dockerfile/haproxy/issues/3"&gt;https://github.com/dockerfile/haproxy/issues/3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.gnu.org/software/libc/manual/html_node/Overview-of-Syslog.html"&gt;http://www.gnu.org/software/libc/manual/html_node/Overview-of-Syslog.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="optmydatahaproxycfg_2"&gt;/opt/mydata/haproxy.cfg&lt;/h4&gt;
&lt;p&gt;This is the more readable config style which separates the frontend from backend and this is haproxy 1.6&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The logging probably does not work =(&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;global
        debug
        log /dev/log local0 info

defaults
        mode http
        timeout connect 5s
        timeout client 5s
        timeout server 5s

frontend myfrontend
        bind *:8443
        default_backend mybackend

backend mybackend
        server s1 example.com:443 ssl verify none
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="starting-the-haproxy-docker-container"&gt;starting the haproxy docker container&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run -p 8443:8443 --rm -it -v /home/admin/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro -v /dev/log:/dev/log --name myhaproxy haproxy:1.6-alpine
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Control + C to quit&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="more-info"&gt;more info&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://registry.hub.docker.com/_/haproxy/"&gt;https://registry.hub.docker.com/_/haproxy/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cbonte.github.io/haproxy-dconv/configuration-1.5.html"&gt;https://cbonte.github.io/haproxy-dconv/configuration-1.5.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cbonte.github.io/haproxy-dconv/configuration-1.6.html"&gt;https://cbonte.github.io/haproxy-dconv/configuration-1.6.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.haproxy.org/git?p=haproxy-1.6.git;a=tree;f=examples"&gt;http://www.haproxy.org/git?p=haproxy-1.6.git;a=tree;f=examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://docs.docker.com/reference/commandline/cli/#adding-entries-to-a-container-hosts-file"&gt;http://docs.docker.com/reference/commandline/cli/#adding-entries-to-a-container-hosts-file&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="virtualization"/><category term="docker"/><category term="haproxy"/></entry><entry><title>Caching data and common gotchas and an intro to redis memcached and varnish</title><link href="https://blog.john-pfeiffer.com/caching-data-and-common-gotchas-and-an-intro-to-redis-memcached-and-varnish/" rel="alternate"/><published>2015-03-26T00:00:00-07:00</published><updated>2015-03-26T00:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2015-03-26:/caching-data-and-common-gotchas-and-an-intro-to-redis-memcached-and-varnish/</id><summary type="html">
&lt;p&gt;&lt;strong&gt;Caching is when you use a copy of a data set rather than using the original source.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Caching often involves a "Key Value Lookup":&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A request is received and the service checks the cache using a Key&lt;/li&gt;
&lt;li&gt;The cache does not contain the Key&lt;/li&gt;
&lt;li&gt;The service generates the result from …&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">
&lt;p&gt;&lt;strong&gt;Caching is when you use a copy of a data set rather than using the original source.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Caching often involves a "Key Value Lookup":&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A request is received and the service checks the cache using a Key&lt;/li&gt;
&lt;li&gt;The cache does not contain the Key&lt;/li&gt;
&lt;li&gt;The service generates the result from the originating data source (i.e. database)&lt;/li&gt;
&lt;li&gt;The service then stores the result in the cache with the Key as the index (and the result as the Value)&lt;/li&gt;
&lt;li&gt;A subsequent request is received and the service checks (looks up) the cache using a Key&lt;/li&gt;
&lt;li&gt;The cache &lt;strong&gt;does&lt;/strong&gt; contain the Key&lt;/li&gt;
&lt;li&gt;The service retrieves the Value from the cache and returns the result&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A more concrete example would be to cache a User object by Email, so that whenever a request came in for a particular Users details the cache would contain their Name, Address, and Phone Number.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Associative_array"&gt;https://en.wikipedia.org/wiki/Associative_array&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="why-cache"&gt;Why Cache?&lt;/h2&gt;
&lt;p&gt;A tradeoff of memory for cpu (or latency or some other business cost).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;computation is expensive (in terms of cpu, time, or money)&lt;/li&gt;
&lt;li&gt;accessing the data from source is too slow&lt;/li&gt;
&lt;li&gt;access to data across a larger geographic distance&lt;/li&gt;
&lt;li&gt;the data actually comes from multiple sources (complex and expensive to retrieve)&lt;/li&gt;
&lt;li&gt;to reduce load on the service originating data&lt;/li&gt;
&lt;li&gt;to reduce contention (i.e. reads not served from the same persistence that does writes)&lt;/li&gt;
&lt;li&gt;server side caching can protect backend resources and improve throughput and performance&lt;/li&gt;
&lt;li&gt;for a client-server architecture, caching on the client reduces the number of required connections to a server&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="questions-to-ask-when-caching"&gt;Questions to ask when caching&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Is the complexity of caching worth the performance gain? (a simpler implementation is often better, less chance of bugs!)&lt;/li&gt;
&lt;li&gt;Does my cache need to be consistent? (meaning the cache and data source return identical results)&lt;/li&gt;
&lt;li&gt;Can my cache be "eventually consistent"? (meaning a wrong answer for some specified period of time is ok)&lt;/li&gt;
&lt;li&gt;Am I caching at a high level? (meaning aggregating a lot of work/responses from lower level systems)&lt;/li&gt;
&lt;li&gt;Am I caching at a low level? (meaning inside of my Data Access Object pattern I'm protecting a single simple resource, i.e. a MySQL table, from being accessed too often)&lt;/li&gt;
&lt;li&gt;How unique are my Keys in my cache (i.e. if multiple users can have the same identifier it would be very bad to return the wrong session to the wrong user)&lt;/li&gt;
&lt;li&gt;Do I have the ability to operate or pay for a caching service?&lt;/li&gt;
&lt;li&gt;What will happen if the cache is unavailable?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cache-latency-times-in-perspective"&gt;Cache Latency Times in Perspective&lt;/h3&gt;
&lt;p&gt;Taking "why cache" to another level, the relative speeds of different cache levels highlight why some applications or algorithms will fail if they do not leverage cache.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If your application uses a very large amount of data the network may actually be better than disk; optimization would probably not be focused on "loop unrolling"&lt;/li&gt;
&lt;li&gt;If your application depends on data across the internet then network caching, routing algorithms, and data modeling (eventual consistency!) may be more important than "tail recursion vs iterative"&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Action&lt;/th&gt;
&lt;th style="text-align: center;"&gt;nanoseconds&lt;/th&gt;
&lt;th style="text-align: center;"&gt;microseconds&lt;/th&gt;
&lt;th style="text-align: center;"&gt;milliseconds&lt;/th&gt;
&lt;th style="text-align: center;"&gt;human scale comparison&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;A typical cpu instruction&lt;/td&gt;
&lt;td style="text-align: center;"&gt;1 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;1 second basis (approximations)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;L1 cache fetch&lt;/td&gt;
&lt;td style="text-align: center;"&gt;0.5 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Branch misprediction&lt;/td&gt;
&lt;td style="text-align: center;"&gt;4 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;L2 cache fetch&lt;/td&gt;
&lt;td style="text-align: center;"&gt;7 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;7 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Mutex lock/unlock&lt;/td&gt;
&lt;td style="text-align: center;"&gt;25 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;RAM "main memory" fetch&lt;/td&gt;
&lt;td style="text-align: center;"&gt;100 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;0.1 us&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;2 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Read 4K randomly from SSD&lt;/td&gt;
&lt;td style="text-align: center;"&gt;100,000 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;100 us&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;28 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Read 1 MB sequentially from memory&lt;/td&gt;
&lt;td style="text-align: center;"&gt;250,000 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;250 us&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;3 days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Send 1000 bytes over 1 Gbps network&lt;/td&gt;
&lt;td style="text-align: center;"&gt;500,000 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;500 us&lt;/td&gt;
&lt;td style="text-align: center;"&gt;0.5 ms&lt;/td&gt;
&lt;td style="text-align: center;"&gt;6 days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Read 1 MB sequentially from SSD&lt;/td&gt;
&lt;td style="text-align: center;"&gt;1,000,000 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;1,000 us&lt;/td&gt;
&lt;td style="text-align: center;"&gt;1 ms&lt;/td&gt;
&lt;td style="text-align: center;"&gt;12 days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Spinning Hard Disk seek&lt;/td&gt;
&lt;td style="text-align: center;"&gt;8,000,000 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;8,000 us&lt;/td&gt;
&lt;td style="text-align: center;"&gt;8 ms&lt;/td&gt;
&lt;td style="text-align: center;"&gt;3 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Read 1 MB sequentially from disk&lt;/td&gt;
&lt;td style="text-align: center;"&gt;20,000,000 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;20,000 us&lt;/td&gt;
&lt;td style="text-align: center;"&gt;20 ms&lt;/td&gt;
&lt;td style="text-align: center;"&gt;7.6 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Packet Roundtrip SF to NY&lt;/td&gt;
&lt;td style="text-align: center;"&gt;70,000,000 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;70,000 us&lt;/td&gt;
&lt;td style="text-align: center;"&gt;70 ms&lt;/td&gt;
&lt;td style="text-align: center;"&gt;2 years&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Packet Roundtrip SF to NY&lt;/td&gt;
&lt;td style="text-align: center;"&gt;150,000,000 ns&lt;/td&gt;
&lt;td style="text-align: center;"&gt;150,000 us&lt;/td&gt;
&lt;td style="text-align: center;"&gt;150 ms&lt;/td&gt;
&lt;td style="text-align: center;"&gt;5 years&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;The L1 cache is the memory cache integrated into the CPU that is closest&lt;/p&gt;
&lt;p&gt;Light travels 30 cm or about 1 foot in 1 nanosecond&lt;/p&gt;
&lt;p&gt;ns = nanoseconds, us = microseconds, ms = milliseconds&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://norvig.com/21-days.html#answers"&gt;http://norvig.com/21-days.html#answers&lt;/a&gt; (Peter Norvig) &lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/CPU_cache"&gt;https://en.wikipedia.org/wiki/CPU_cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Solid-state_drive#Controller"&gt;https://en.wikipedia.org/wiki/Solid-state_drive#Controller&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.codingblocks.net/podcast/episode-45-caching-overview-and-hardware/"&gt;http://www.codingblocks.net/podcast/episode-45-caching-overview-and-hardware/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wondernetwork.com/pings"&gt;https://wondernetwork.com/pings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/rzezeski/status/398306728263315456/photo/1"&gt;https://twitter.com/rzezeski/status/398306728263315456/photo/1&lt;/a&gt; (Brendan Gregg)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="caches-are-another-operational-component-with-overhead"&gt;Caches are another Operational component with Overhead&lt;/h3&gt;
&lt;p&gt;The best advice is to definitely avoiding caching until the last possible moment (&lt;em&gt;"less is the best" and "premature optimization" and "be future flexible" and "defer architecture decisions"&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;Not only do you have to write code complexity for using a cache, there's the nitty gritty of running a cache (which can be a completely different expertise than programming)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install&lt;/li&gt;
&lt;li&gt;Deployment&lt;/li&gt;
&lt;li&gt;Upgrades&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;Metrics&lt;/li&gt;
&lt;li&gt;Testing (i.e. synthetic smoke tests or load)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this operational cost is free, and there are plenty of issues when just implementing caching in the code...&lt;/p&gt;
&lt;h2 id="high-level-caching-for-the-application-versus-low-level-caching-for-the-persistence-layer"&gt;"High Level" caching for the application versus "Low Level" caching for the persistence layer&lt;/h2&gt;
&lt;p&gt;Caching can be the most effective at the "highest" layer where the application is able to trivially service a request.  (e.g. caching the final full web page that was requested)&lt;/p&gt;
&lt;p&gt;This "protects" all of the underlying machinery from having to do work.  It requires a good understanding of how dynamic or personalized the results are, designing the system to have pieces that can be cached, and is brittle to change.&lt;/p&gt;
&lt;p&gt;Caching can be an "easy win" when applied at the "lowest" layer right near the persistence (i.e. redis "in front of" a database - that for example contains the new articles that will be served on a web page) as all of the components in layers above can benefit from improved performance without having to change the business logic (or perhaps even unaware of the caching).&lt;/p&gt;
&lt;p&gt;Both can be used together but to paraphrase: "mo caching mo problems"&lt;/p&gt;
&lt;h2 id="how-to-cache"&gt;How to Cache&lt;/h2&gt;
&lt;h3 id="cache-on-write"&gt;Cache on Write&lt;/h3&gt;
&lt;p&gt;Also known as "cache on write through", whenever new data is written a cache must also be updated.&lt;/p&gt;
&lt;h3 id="cache-on-read"&gt;Cache on Read&lt;/h3&gt;
&lt;p&gt;Also known as "cache on read through", whenever a query is made first the cache is checked.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If there is a "cache miss" then the data source is queried and the cache is updated and the result is returned.  &lt;/li&gt;
&lt;li&gt;If there is a "cache hit" and the data is in the cache then it is returned (and potentially a cache key expiration updated as this cache hit improved the cache efficiency).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cache-warming"&gt;Cache Warming&lt;/h3&gt;
&lt;p&gt;Pre-emptively adding data to the cache is "cache warming" in order to improve "cache hit" percentages and reduce the risk of "cold cache" issues.&lt;/p&gt;
&lt;h3 id="flush-the-cache"&gt;Flush the Cache&lt;/h3&gt;
&lt;p&gt;Removing some or all data from the cache in order to invalidate a chunk of data (i.e. all users need to reset their passwords) or pre-emptively free up memory/space.&lt;/p&gt;
&lt;h2 id="common-gotchas"&gt;Common Gotchas&lt;/h2&gt;
&lt;p&gt;Caching is challenging because of the need for data consistency, parallel requests, and race conditions.&lt;/p&gt;
&lt;p&gt;One good way to think about it is a banking system with money...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If two people both try to empty a bank account at an ATM at the same time how will your caching system handle it?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="cache-on-write-gotchas"&gt;Cache on write gotchas&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;There may be a design mismatch as since data is only cached on write, if reads are occurring mostly on data written a long time ago they will be expired/pushed out and you will have poor cache efficiency (paradoxically adding caching will have resulted in more complexity and worse performance).&lt;/li&gt;
&lt;li&gt;One implementation flaw is updating the cache first; if the update to the data source fails then some requests may have been given incorrect data.&lt;/li&gt;
&lt;li&gt;The opposite order of updating the canonical source first can have a similar problem if the process fails before the cache can be updated. (Thus leaving the cache with old data).&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;First invalidating the cache reduces the risk of a failure during a write creating an inconsistent state&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Next update the data persistence (which should be provided as an atomic operation by your vendor ;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Finally update the cache&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;In the worst case the canonical source will be updated without the benefit of caching&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;One might also have a "transaction" defined around both the update of the origin and cache with retries for failure&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;While "cache on write" is a sometimes band-aid for NoSQL "eventual consistency" when it fails (i.e. all applications should expect that a cache will not exist or have a cache miss) the result may be data inconsistency.&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;One workaround is "check and set" (or "compare and set") where the cache will auto-invalidate if two conflicting entries are attempted.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://neopythonic.blogspot.com/2011/08/compare-and-set-in-memcache.html"&gt;https://neopythonic.blogspot.com/2011/08/compare-and-set-in-memcache.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;This "gotcha" could be summarized as not handling rollback&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="expiration-a-cache-full-of-stale-junk"&gt;Expiration: a cache full of stale junk&lt;/h3&gt;
&lt;p&gt;A naive implementation of caching will store every result in the cache forever...&lt;/p&gt;
&lt;p&gt;While this seems like a good idea (&lt;em&gt;"The cache application/service will just evict unused items based some algorithm"&lt;/em&gt;) it is essentially forcing your cache to be full of potentially low value information on the hope that someone else will solve the problem.&lt;/p&gt;
&lt;p&gt;Since some caching tools/framework do not set a default &lt;strong&gt;Time To Live&lt;/strong&gt; or &lt;strong&gt;Expiration&lt;/strong&gt; and in that case all of your data may quickly fill up the cache (not a bad thing per se), but then it will use whatever default or global "eviction policy" that is defined.&lt;/p&gt;
&lt;p&gt;Even O(1) can be broken by a pathological data set, and keeping every item seems like a good way to find an edge case (i.e. hash collisions and chaining).&lt;/p&gt;
&lt;p&gt;Applying business logic and empirical data to pick sane expiration values might not only improve cache performance but may protect your service from security issues or bugs due to serving really stale data.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;e.g. for security reasons, &lt;strong&gt;caching a session "forever" is a bad idea&lt;/strong&gt; as an attacker may get access to an old client cache or token and be able to impersonate a legimate user&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Issues with Expiration Set Too Long:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Security concerns&lt;/li&gt;
&lt;li&gt;Lack of control/non determinism for when and what items might be evicted&lt;/li&gt;
&lt;li&gt;Poor performance, memory pressure, and possibly increased operational cost&lt;/li&gt;
&lt;li&gt;Stale data&lt;/li&gt;
&lt;li&gt;Large cache sizes may end up writing to disk (i.e. redis sync to disk may use copy on write)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Set a TTL or Expiration, whenever possible, that matches your domain&lt;/strong&gt; (i.e. for a session 1 day or 1 week).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the Time to Live is too short then the cache may have very poor efficiency (items expire before they can generate even one cache hit), meaning all of the coding and operational cost are for nothing =[&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="cold-cache-and-the-thundering-herd"&gt;Cold Cache and the Thundering Herd&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;If the cache is "cold", i.e. has not been populated, then all queries will go directly to the source&lt;/li&gt;
&lt;li&gt;If the source is not prepared for the "thundering herd" of requests (that were usually handled by the cache) then the source may become overloaded and bad things will happen&lt;/li&gt;
&lt;li&gt;It is therefore best practice to "warm the cache" by seeding data from the source into the cache before significant load events&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Cold cache not only can cause problems from the source but when lot of data is written simultaneously to the cache, if the cache uses underlying disk or some other IO resource, it may temporarily overwhelm the cache (system/framework).&lt;/p&gt;
&lt;h3 id="upgrading-your-application"&gt;Upgrading your application&lt;/h3&gt;
&lt;p&gt;In a sense the cache layer is an external persistence that has to stay in sync with the application code; they are logically and semantically bound together.&lt;/p&gt;
&lt;p&gt;Modification to your application code, specifically the way it reads and writes to the cache, may return "bad" data.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Cache key "admins" stored a list of usernames of admin users for the application&lt;/li&gt;
&lt;li&gt;Cache key "ausers" stored a list of usernames that begin with the letter "a"&lt;/li&gt;
&lt;li&gt;An application upgrade occurs&lt;/li&gt;
&lt;li&gt;Now the code has a bug that looks up "ausers" in order to give administrator permssions&lt;/li&gt;
&lt;li&gt;(oops)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="spanning-multiple-keys"&gt;Spanning Multiple Keys&lt;/h3&gt;
&lt;p&gt;If you need to retrieve multiple items from a cache in order to fulfill a request you may run the risk of "torn reads" where the first item retrieved from cache is logically inconsistent with the second item.&lt;/p&gt;
&lt;h2 id="tools-for-caching"&gt;Tools for caching&lt;/h2&gt;
&lt;p&gt;Much like encryption it is probably a good idea to use a time tested caching component over writing your own implementation.&lt;/p&gt;
&lt;p&gt;A local in memory cache is a tried and true way of speeding up an application but it may not provide the transparency and visibility when there are bugs.&lt;/p&gt;
&lt;p&gt;While it seems trivial to setup it will slow down your dev velocity on your high value focus area and every new feature you realize you need (automatic expiration, authentication, etc.) will create a distraction and eventual maintenance requirement.&lt;/p&gt;
&lt;p&gt;Instead there are quite a few very popular battle tested options...&lt;/p&gt;
&lt;h3 id="memcached"&gt;Memcached&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://memcached.org"&gt;http://memcached.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Memcached"&gt;https://en.wikipedia.org/wiki/Memcached&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/r/_/memcached/"&gt;https://hub.docker.com/r/_/memcached/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;docker run --rm -it --publish 11211:11211 --name mymemcached memcached:alpine&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;echo -e 'add foo 0 60 11\r\nhello world\r' | nc localhost 11211
telnet localhost 11211
get foo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;VALUE foo 0 11
hello world&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/memcached/memcached/wiki/Commands"&gt;https://github.com/memcached/memcached/wiki/Commands&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="redis-examples"&gt;Redis Examples&lt;/h3&gt;
&lt;p&gt;Redis has surpassed memcached in terms of speed and functionality and if you need to store more than "just a string" you should experiment with it.&lt;/p&gt;
&lt;p&gt;Besides having a cache to speed up lookups for your application or as a globally shared cache (be careful!) between multiple application serveers there can be a nice convenience as a "meta" persistence such that you can deploy a new version of your application and not lose all of the data in the cache.&lt;/p&gt;
&lt;p&gt;One thing to think about is that local redis might be far more effective than remote over the network redis.&lt;/p&gt;
&lt;p&gt;If your application can depend less shared state this is good because sharing is a nightmare for cache semantics and distributed computing.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When possible avoid a globally shared cache between multiple processes or servers, or invest in learning about atomic operations&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Regardless of whether you securing your remote cache (or just depend on network isolation) you will always want to measure cache effectiveness.&lt;/p&gt;
&lt;h3 id="installing-redis"&gt;Installing Redis&lt;/h3&gt;
&lt;p&gt;The simplest way is to use Docker, &lt;a href="https://hub.docker.com/r/_/redis/"&gt;https://hub.docker.com/r/_/redis/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker run --rm -it --publish 6379:6379 --name myredis redis:alpine&lt;/code&gt;
&lt;code&gt;docker run -it --link myredis:redis --rm redis:alpine redis-cli -h redis -p 6379 set message hello&lt;/code&gt;
&lt;code&gt;docker run -it --link myredis:redis --rm redis:alpine redis-cli -h redis -p 6379 get message&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;run an ephemeral docker container and then non-interactively use the same docker image to set and get a string key&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you prefer installing locally to your filesystem or server:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redis.io/topics/quickstart"&gt;https://redis.io/topics/quickstart&lt;/a&gt; (compiling from source)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://packages.ubuntu.com/trusty/redis-server"&gt;https://packages.ubuntu.com/trusty/redis-server&lt;/a&gt; (sudo apt-get install redis-server)&lt;/p&gt;
&lt;p&gt;redis-cli -h localhost:6379 ping&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PONG , aka verify a remote server connectivity&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="interactive-redis-prompt"&gt;Interactive Redis Prompt&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;redis-cli
keys *
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="non-interactive-redis-commands"&gt;Non Interactive Redis Commands&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;redis-cli KEYS &lt;span class="gs"&gt;*:*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;non-interactively get all of the keys that have subkeys&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;redis-cli KEYS "session:3:*" | xargs redis-cli DEL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;non-interactively delete/remove all of the subkeys under the sub subkey&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;redis-cli KEYS session:1:*
redis-cli hgetall session:1:web
redis-cli hgetall session:1:web:presence

redis-cli KEYS session:1:*  | grep session:1:web-48
  session:1:web-48679:rooms
  session:1:web-48679:presence
  session:1:web-48679:message_ids
  session:1:web-48679

redis-cli zrange sessions:1 0 9
  1) "1:web"
  2) "1:web-48679"

redis-cli zrem sessions:1 1:web-48679
redis-cli del   session:1:web-48679:rooms
redis-cli del   session:1:web-48679:presence
redis-cli del   session:1:web-48679:message_ids
redis-cli del   session:1:web-48679
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redis.io/commands"&gt;https://redis.io/commands&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="redis-clients"&gt;Redis Clients&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install redis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://redis.io/clients#python"&gt;http://redis.io/clients#python&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;redis&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StrictRedis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flushall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://pypi.python.org/pypi/redis"&gt;https://pypi.python.org/pypi/redis&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Go&lt;/strong&gt; &lt;code&gt;go get github.com/garyburd/redigo&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;"github.com/garyburd/redigo/redis"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Dial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;":6379"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"hi"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A more complete example: &lt;a href="https://bitbucket.org/johnpfeiffer/go-cache"&gt;https://bitbucket.org/johnpfeiffer/go-cache&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="varnish"&gt;Varnish&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.varnish-cache.org/about"&gt;https://www.varnish-cache.org/about&lt;/a&gt; REST web caching&lt;/li&gt;
&lt;/ul&gt;</content><category term="programming"/><category term="cache"/><category term="caching"/><category term="redis"/><category term="memcached"/><category term="varnish"/></entry><entry><title>Subunit and Subunit2JunitXML to get JUnitXML test result output from UnitTest</title><link href="https://blog.john-pfeiffer.com/subunit-and-subunit2junitxml-to-get-junitxml-test-result-output-from-unittest/" rel="alternate"/><published>2014-11-01T00:00:00-07:00</published><updated>2014-11-01T00:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-11-01:/subunit-and-subunit2junitxml-to-get-junitxml-test-result-output-from-unittest/</id><summary type="html">
&lt;p&gt;Test results from differing systems or multiple test runs need a common format.&lt;/p&gt;
&lt;p&gt;JUnit XML is almost a de facto standard for test results given almost all major Continuous Integration products support it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confluence.atlassian.com/display/BAMBOO/JUnit+parsing+in+Bamboo"&gt;https://confluence.atlassian.com/display/BAMBOO/JUnit+parsing+in+Bamboo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.fossology.org/projects/fossology/wiki/Junit_xml_format"&gt;http://www.fossology.org/projects/fossology/wiki …&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">
&lt;p&gt;Test results from differing systems or multiple test runs need a common format.&lt;/p&gt;
&lt;p&gt;JUnit XML is almost a de facto standard for test results given almost all major Continuous Integration products support it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confluence.atlassian.com/display/BAMBOO/JUnit+parsing+in+Bamboo"&gt;https://confluence.atlassian.com/display/BAMBOO/JUnit+parsing+in+Bamboo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.fossology.org/projects/fossology/wiki/Junit_xml_format"&gt;http://www.fossology.org/projects/fossology/wiki/Junit_xml_format&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://pytest.org/latest/usage.html"&gt;http://pytest.org/latest/usage.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="setup"&gt;Setup&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;pip install python-subunit junitxml&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;assuming virtuelnv and myenv/bin/activate , junitxml is a hidden dependency :(&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Do not use &lt;code&gt;apt-get install subunit&lt;/code&gt; as even with 14.04 Ubuntu it has an older version does not contain timings and subunit2junitxml creates "skip" instead of "skipped"&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="example-unittest-class"&gt;Example UnitTest Class&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;unittest&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;john&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skipIf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'always skip'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="example-usage"&gt;Example Usage&lt;/h2&gt;
&lt;h3 id="one-liner"&gt;One Liner&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;python -m subunit.run foo | subunit2junitxml --no-passthrough --output-to test-results&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;forward = non-subunit output will be encapsulated in subunit &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="intermediate-subunit-results-file"&gt;Intermediate Subunit Results File&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;python -m subunit.run test_some_filename_with_py_truncated &amp;gt; test-results.subunit&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do not use python -m subunit.run test_some_filename_with_py_truncated to stdout as it expects to have binary delimiters which screw up the console command line&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;subunit-ls &amp;lt; test-results.subunit
subunit-stats &amp;lt; test-results.subunit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;python -m subunit.run foo &amp;gt;&amp;gt; test-results.subunit&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;append some more test results
&lt;code&gt;subunit-stats &amp;lt; test-results.subunit&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;subunit2junitxml --no-passthrough --output-to test-results.xml &amp;lt; test-results.subunit&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;no passthrough does not pass/convert any extraneous non subunit data/lines to the junit xml&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;testsuite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;errors=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;failures=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;tests=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;time=&lt;/span&gt;&lt;span class="s"&gt;"0.001"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;testcase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;classname=&lt;/span&gt;&lt;span class="s"&gt;"john.john"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"test_fail"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;time=&lt;/span&gt;&lt;span class="s"&gt;"0.000"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;failure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"testtools.testresult.real._StringException"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;_StringException:&lt;span class="w"&gt; &lt;/span&gt;Traceback&lt;span class="w"&gt; &lt;/span&gt;(most&lt;span class="w"&gt; &lt;/span&gt;recent&lt;span class="w"&gt; &lt;/span&gt;call&lt;span class="w"&gt; &lt;/span&gt;last):
&lt;span class="w"&gt;  &lt;/span&gt;File&lt;span class="w"&gt; &lt;/span&gt;"john.py",&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;9,&lt;span class="w"&gt; &lt;/span&gt;in&lt;span class="w"&gt; &lt;/span&gt;test_fail
&lt;span class="w"&gt;    &lt;/span&gt;self.assertTrue(False)
&lt;span class="w"&gt;  &lt;/span&gt;File&lt;span class="w"&gt; &lt;/span&gt;"/usr/lib/python2.7/unittest/case.py",&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;424,&lt;span class="w"&gt; &lt;/span&gt;in&lt;span class="w"&gt; &lt;/span&gt;assertTrue
&lt;span class="w"&gt;    &lt;/span&gt;raise&lt;span class="w"&gt; &lt;/span&gt;self.failureException(msg)
AssertionError:&lt;span class="w"&gt; &lt;/span&gt;False&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;true

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/failure&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/testcase&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;testcase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;classname=&lt;/span&gt;&lt;span class="s"&gt;"john.john"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"test_skip"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;time=&lt;/span&gt;&lt;span class="s"&gt;"0.000"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;skipped&amp;gt;&lt;/span&gt;always&lt;span class="w"&gt; &lt;/span&gt;skip&lt;span class="nt"&gt;&amp;lt;/skipped&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/testcase&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;testcase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;classname=&lt;/span&gt;&lt;span class="s"&gt;"john.john"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"test_success"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;time=&lt;/span&gt;&lt;span class="s"&gt;"0.000"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/testsuite&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="twisted-unittesting"&gt;Twisted UnitTesting&lt;/h3&gt;
&lt;p&gt;`trial --reporter=subunit foo | subunit2junitxml --forward --output-to=junitxml-result.xml&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ImportError: No module named 'junitxml'&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;You may not have installed the junitxml module which subunit apparently sometimes depends on: &lt;code&gt;pip install junitxml&lt;/code&gt; &lt;em&gt;use sudo only if not using virtualenv&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;AttributeError: 'AutoTimingTestResultDecorator' object has no attribute 'errors'&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;This occured becaused TestSomeClass(unittest.TestCase) definition had an errors property/attribute which resulted in a namespace collision =(&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Empty results like this: &amp;lt;testsuite errors="0" failures="0" name="" tests="0" time="0.003"&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;if you view/cat your results.subunit you will notice:&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;test: directory.path.foobar.FooBar.test_constructor&lt;/p&gt;
&lt;p&gt;successful: directory.path.foobar.FooBar.test_constructor&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;That is old subunit output (i.e. an old version of Twisted: trial --reporter=subunit), the new version 2 uses non printable characters instead of newlines (which sometimes ruins output to console)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Resolution for old subunit version converted to new subunit version&lt;/strong&gt;:&lt;blockquote&gt;
&lt;p&gt;trial --reporter=subunit foo | subunit-1to2 &amp;gt;&amp;gt; /tmp/results.subunit ; subunit2junitxml --no-passthrough --output-to test-results.xml &amp;lt; /tmp/test-results.subunit&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="more-info"&gt;More Info&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.tech-foo.net/making-the-most-of-subunit.html"&gt;http://www.tech-foo.net/making-the-most-of-subunit.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.python.org/pypi/python-subunit"&gt;https://pypi.python.org/pypi/python-subunit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://pypi.python.org/pypi/junitxml"&gt;https://pypi.python.org/pypi/junitxml&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://launchpad.net/subunit"&gt;https://launchpad.net/subunit&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content><category term="programming"/><category term="python"/><category term="subunit"/><category term="subunit2junitxml"/><category term="unittest"/><category term="testing"/></entry><entry><title>Virtualenv Python Interpreter from source</title><link href="https://blog.john-pfeiffer.com/virtualenv-python-interpreter-from-source/" rel="alternate"/><published>2014-10-31T00:00:00-07:00</published><updated>2014-10-31T00:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-10-31:/virtualenv-python-interpreter-from-source/</id><summary type="html">
&lt;p&gt;When building an application (including an external facing webapp or an internal test suite) it is critical to manage your dependencies.&lt;/p&gt;
&lt;p&gt;Virtualenv is a tool that keeps all the dependencies in a file system based container (and overcomes permissions based issues as well).&lt;/p&gt;
&lt;p&gt;To really isolate your application from the …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;When building an application (including an external facing webapp or an internal test suite) it is critical to manage your dependencies.&lt;/p&gt;
&lt;p&gt;Virtualenv is a tool that keeps all the dependencies in a file system based container (and overcomes permissions based issues as well).&lt;/p&gt;
&lt;p&gt;To really isolate your application from the environment not only do you need a specific version of libraries (i.e. you know your application works fine with requests 2.4.3 and selenium 2.44) but additionally a specific version of the Python Interpreter.&lt;/p&gt;
&lt;h2 id="build-python-from-source"&gt;Build python from source&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;wget https://www.python.org/ftp/python/2.7.8/Python-2.7.8.tgz
tar -xf Python-2.7.8.tar.gz
cd Python-2.7.8
./configure --prefix=/home/ubuntu/python --enable-unicode=ucs4
make &amp;amp;&amp;amp; make altinstall

/home/ubuntu/python/bin/python2.7 --version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;altinstall ensures we do not try to override the existing /usr/bin/python binary which can be important if you want python 2.7.3 and python 2.7.8 to exist side by side&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Optionally: &lt;code&gt;echo 'alias py="/home/ubuntu/python/bin/python2.7"' &amp;gt;&amp;gt; ~/.bashrc&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="python3-from-source"&gt;python3 from source&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;wget https://www.python.org/ftp/python/3.4.2/Python-3.4.2.tgz
tar -xf Python-3.4.2.tar.gz
cd Python-3.4.2
./configure --prefix=/opt/python3.4.2 &amp;amp;&amp;amp; make -j$(nproc) &amp;amp;&amp;amp; make altinstall
ls -ahl /usr/local/bin | grep 3.4

/usr/local/bin/pip3.4 install --upgrade virtualenv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="if-you-mess-up-your-os-level-python"&gt;If you mess up your OS level python&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;apt-get install python3&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python3 depends on dh-python; however:
Package dh-python is not configured yet.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Look at the stack trace, reinstalling may not have put all of the helper directory and .py files in place&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;File "/usr/lib/python3.4/site.py", line 586, in &amp;lt;module&amp;gt;
ImportError: No module named '_sysconfigdata_m'

mv /usr/lib/python3.4 /usr/lib/python3.4-OLD
wget http://mirrors.kernel.org/ubuntu/pool/main/d/dh-python/dh-python_1.20140128-1ubuntu8_all.deb
dpkg -i --force-depends dh-python_1.20140128-1ubuntu8_all.deb
apt-get install python3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And in my case I needed to reinstall loads of python3 stuff: &lt;code&gt;apt-get install xubuntu-destkop&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="installing-virtualenv"&gt;Installing virtualenv&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;sudo pip install --upgrade virtualenv&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;getting the latest version of virtualenv as any OS packages are likely to be outdated
alternatively you can go all out and just use virtualenv locally from source&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="virtualenv-from-source"&gt;virtualenv from source&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;wget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//pypi.python.org/packages/source/v/virtualenv/virtualenv-X.X.tar.gz&lt;/span&gt;
&lt;span class="nx"&gt;tar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;xf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;virtualenv&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gz&lt;/span&gt;
&lt;span class="nx"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;virtualenv&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;python&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;python2&lt;/span&gt;&lt;span class="m m-Double"&gt;.7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;virtualenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myvenv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;does not require sudo and works around path or permissions requirements&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="example-usage"&gt;Example Usage&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;virtualenv myvenv&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;creates a local copy of required files like the python interpreter and its own version of pip&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;myvenv
| -- bin
     | -- activate
     | -- easy_install
     | -- pip
     | -- python
| -- include
| -- lib
     | -- python2.7
         | -- site-packages
              | -- pip
              | -- setuptools
| -- local
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;myenv/bin/pip install --upgrade requests&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;no sudo was required to add locally myvenv/lib/python2.7/site-packages/requests&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;requests&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__version__&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="using-virtualenv-with-a-specific-python-version-or-binary"&gt;Using virtualenv with a specific python version or binary&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;virtualenv -p python3.5 venv
source venv/bin/activate
python --version
which pip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Python 3.5.0+
the shortest example to use the OS python3.5 binary when creating the venv directory with the virtual environment&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;virtualenv --python=/home/ubuntu/python/bin/python2.7 myvenv
myenv/bin/python
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Python 2.7.8
the advanced example uses a python binary that was created from source to ensure the application does not suffer when the OS has a python upgrade (or your libraries need a newer version of python than provided)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;virtualenv --version&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;just in case your version of virtualenv has a bug and needs to be upgraded first&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="activate-and-deactivate-to-update-your-environment-temporarily"&gt;activate and deactivate to update your environment temporarily&lt;/h3&gt;
&lt;p&gt;Rather than using the explicit paths (which is the most clear but cumbersome) you can override your shell Environment:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/usr/bin/python --version&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2.7.3&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;source myenv/bin/activate&lt;/code&gt;
&lt;code&gt;python --version&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2.7.8&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;pip install requests&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;no sudo was required to add locally myvenv/lib/python2.7/site-packages/requests&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;deactivate&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="more-info"&gt;More Info&lt;/h2&gt;
&lt;p&gt;When using git make sure .gitignore contains the "myenv" directory as you do not want to store these binaries in version control.&lt;/p&gt;
&lt;p&gt;Typically Heroku or other PaaS allow you to specify a python interpreter version and library requirements in a configuration file.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://virtualenv.readthedocs.org/en/latest/virtualenv.html"&gt;http://virtualenv.readthedocs.org/en/latest/virtualenv.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.python.org/downloads"&gt;https://www.python.org/downloads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/common-python-tools-using-virtualenv-installing-with-pip-and-managing-packages"&gt;https://www.digitalocean.com/community/tutorials/common-python-tools-using-virtualenv-installing-with-pip-and-managing-packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="build-CI-CD-devops"/><category term="python"/><category term="python3"/><category term="pip"/><category term="virtualenv"/><category term="dependency management"/></entry><entry><title>Xubuntu hotkey shortcuts and Zoom (also for xfce)</title><link href="https://blog.john-pfeiffer.com/xubuntu-hotkey-shortcuts-and-zoom-also-for-xfce/" rel="alternate"/><published>2014-10-08T16:00:00-07:00</published><updated>2014-10-08T16:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-10-08:/xubuntu-hotkey-shortcuts-and-zoom-also-for-xfce/</id><summary type="html">
&lt;p&gt;Interacting with the computer is so much faster with keyboard hotkey shortcuts and other tricks, these are applicable as of Xubuntu 14.04&lt;/p&gt;
&lt;h3 id="xubuntu-zoom-magnifier"&gt;Xubuntu Zoom (magnifier)&lt;/h3&gt;
&lt;p&gt;Alt + scrollwheel up to Zoom in &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;or on a laptop with a touchpad two finger swipe up&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Alt + scrollwheel down to Zoom out&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;or …&lt;/p&gt;&lt;/blockquote&gt;</summary><content type="html">
&lt;p&gt;Interacting with the computer is so much faster with keyboard hotkey shortcuts and other tricks, these are applicable as of Xubuntu 14.04&lt;/p&gt;
&lt;h3 id="xubuntu-zoom-magnifier"&gt;Xubuntu Zoom (magnifier)&lt;/h3&gt;
&lt;p&gt;Alt + scrollwheel up to Zoom in &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;or on a laptop with a touchpad two finger swipe up&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Alt + scrollwheel down to Zoom out&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;or on a laptop with a touchpad two finger swipe down&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="hotkey-application-shortcuts"&gt;Hotkey Application Shortcuts&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Mouse (start button) -&amp;gt; Settings -&amp;gt; Settings Manager -&amp;gt; Keyboard -&amp;gt; Application Shortcuts&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Add -&amp;gt; sh -c "sleep 1 &amp;amp;&amp;amp; xset dpms force off" -&amp;gt; OK (will open another popup)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;press control + alt + q on the keyboard (the popup will go away and the hotkeys will be saved)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Double click on the command in the Command column to edit the command&lt;/p&gt;
&lt;p&gt;Double click on the hotkeys in the Shortcut column to modify the hotkey combination&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sleep 1 &amp;amp;&amp;amp; xset dpms force off        control + alt + q&lt;/li&gt;
&lt;li&gt;exo-open --launch TerminalEmulator    control + alt + t&lt;/li&gt;
&lt;li&gt;gnome-calculator          control + alt + g           &lt;/li&gt;
&lt;li&gt;/usr/bin/galculator           control + alt + g&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;/usr/bin/leafpad --tab-width=4        control + alt + f  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;blockquote&gt;
&lt;p&gt;vi /usr/share/applications/leafpad.desktop  Exec=leafpad --tab-width=4 %f&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;/usr/bin/chromium-browser     control + alt + a&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;/usr/bin/filezilla            control + alt + i&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;/opt/pycharm/bin/pycharm.sh       control + alt + p&lt;/li&gt;
&lt;li&gt;xfce4-screenshooter -r            printscreen&lt;/li&gt;
&lt;li&gt;
&lt;blockquote&gt;
&lt;p&gt;select region (-f fullscreen , -w active window)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;amixer set Master 5%- -q          Alt + Down&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;amixer set Master 5%+ -q          Alt + Up&lt;/li&gt;
&lt;li&gt;xflock4               control + alt + delete&lt;/li&gt;
&lt;li&gt;retext                    control + alt + r&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;/home/ubuntu/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-keyboard-shortcuts.xml&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;/etc/xdg/menus/xfce-applications.menu&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="xfce-keyboard-shortcuts"&gt;XFCE Keyboard Shortcuts&lt;/h3&gt;
&lt;p&gt;Settings Editor -&amp;gt; xfce4-keyboard-shortcuts&lt;/p&gt;
&lt;p&gt;commands -&amp;gt; custom... New&lt;/p&gt;
&lt;p&gt;/commands/custom/&lt;control&gt;&lt;alt&gt;f
leafpad&lt;/alt&gt;&lt;/control&gt;&lt;/p&gt;
&lt;p&gt;/xfwm4/custom/&lt;control&gt;&lt;alt&gt;d&lt;/alt&gt;&lt;/control&gt;&lt;/p&gt;
&lt;p&gt;show_desktop_key&lt;/p&gt;
&lt;p&gt;/commands/custom/&lt;super&gt;e&lt;/super&gt;&lt;/p&gt;
&lt;p&gt;mousepad&lt;/p&gt;
&lt;p&gt;exo-open --launch FileManager&lt;/p&gt;
&lt;h3 id="ubuntu-keyboard-shortcuts"&gt;Ubuntu Keyboard Shortcuts&lt;/h3&gt;
&lt;p&gt;ubuntu 12.04 keyboard shortcuts&lt;/p&gt;
&lt;p&gt;System Settings -&amp;gt; Keyboard (may not be visible so type it in the search box) -&amp;gt; Shortcuts &lt;/p&gt;
&lt;p&gt;Either modify an existing shortcut (i.e. disable one that is annoying)&lt;/p&gt;
&lt;p&gt;OR Custom Shortcuts -&amp;gt; + (add a new one)&lt;/p&gt;
&lt;p&gt;e.g.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;
&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sr"&gt;/usr/bin/&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then highlight the row (clicking on the right area where it's "disabled") and type the key combination desired to trigger the shortcut (e.g. control + alt + a)&lt;/p&gt;
&lt;p&gt;HINTS: &lt;code&gt;sudo find / -iname "*chromium*"&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo which chromium-browser&lt;/code&gt;&lt;/p&gt;</content><category term="linux"/></entry><entry><title>Static site pelican blog with GitHub Pages and Travis CI</title><link href="https://blog.john-pfeiffer.com/static-site-pelican-blog-with-github-pages-and-travis-ci/" rel="alternate"/><published>2014-09-15T04:04:00-07:00</published><updated>2014-09-15T04:04:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-09-15:/static-site-pelican-blog-with-github-pages-and-travis-ci/</id><summary type="html">
&lt;p&gt;Setting up a static blog site I decided to follow some great Dev Ops principles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Convention over customization&lt;/li&gt;
&lt;li&gt;Minimal infrastructure to maintain&lt;/li&gt;
&lt;li&gt;Leverage the cloud (from reliable vendors)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, being "an efficient engineer" I had the extra hard requirement of "free" =p&lt;/p&gt;
&lt;p&gt;Related articles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/how-to-set-up-a-pelican-static-blog-site/"&gt;https://blog.john-pfeiffer.com/how-to-set-up-a-pelican-static-blog-site/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/static-site-with-bitbucket-and-shippable-and-pelican/"&gt;https …&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">
&lt;p&gt;Setting up a static blog site I decided to follow some great Dev Ops principles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Convention over customization&lt;/li&gt;
&lt;li&gt;Minimal infrastructure to maintain&lt;/li&gt;
&lt;li&gt;Leverage the cloud (from reliable vendors)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, being "an efficient engineer" I had the extra hard requirement of "free" =p&lt;/p&gt;
&lt;p&gt;Related articles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/how-to-set-up-a-pelican-static-blog-site/"&gt;https://blog.john-pfeiffer.com/how-to-set-up-a-pelican-static-blog-site/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/static-site-with-bitbucket-and-shippable-and-pelican/"&gt;https://blog.john-pfeiffer.com/static-site-with-bitbucket-and-shippable-and-pelican/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="github-setup"&gt;Github Setup&lt;/h2&gt;
&lt;p&gt;Sign in to GitHub , &lt;a href="https://github.com/login"&gt;https://github.com/login&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Create two new &lt;strong&gt;public repos&lt;/strong&gt;: username.github.io and source.username.github.io&lt;/p&gt;
&lt;p&gt;In source.username.github.io you will put the source markdown and theme etc. from the pelican static blog.&lt;/p&gt;
&lt;p&gt;The username.github.io will be where the output pelican transformed .html is automatically pushed by Travis CI and is conveniently served by GitHub pages. &lt;a href="https://pages.github.com/"&gt;https://pages.github.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ensure &lt;a href="https://travis-ci.org"&gt;https://travis-ci.org&lt;/a&gt; is authorized by clicking on the Configure button, &lt;a href="https://docs.travis-ci.com/user/getting-started/"&gt;https://docs.travis-ci.com/user/getting-started/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(You can review the OAuth apps with &lt;a href="https://github.com/settings/applications"&gt;https://github.com/settings/applications&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;While the Integration allows Travis CI to detect commits to your repositories it does not necessarily allow it to push changes into a repository, for that we'll use an OAuth Token.&lt;/p&gt;
&lt;p&gt;Create a new personal oauth token: &lt;a href="https://github.com/settings/tokens/new"&gt;https://github.com/settings/tokens/new&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;scope should be public repos only&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;The long way is to use the GitHub WebUI -&amp;gt; Applications -&amp;gt; Personal Access Token -&amp;gt; public_repo (only)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Make sure you store the personal access token in a password manager or somewhere safe (i.e. not in plaintext in your email or published on your website ;)&lt;/p&gt;
&lt;h2 id="travis-ci-setup"&gt;Travis CI Setup&lt;/h2&gt;
&lt;p&gt;The beauty of these tightly integrated continuous integration systems is that when a commit is pushed into a specific github repo you can trigger some command execution, in this case to convert the markdown to html and then push it to a different repository. (The github pages special repository which is why it must be specifically username.github.io)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I use &lt;strong&gt;travis-ci.org&lt;/strong&gt; which is free for public repos, travis-ci.com is the paid professional service for private repositories&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Register the repository and github personal access token in TravisCI ...&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;From the Travis Side also "Authorize Application" using https://travis-ci.org/profile/yourusername&lt;/li&gt;
&lt;li&gt;Find the list of repositories (you may have to first click "sync now" to see the list)&lt;/li&gt;
&lt;li&gt;Slide to ON (checkmark) for the source.username.github.io repository&lt;/li&gt;
&lt;li&gt;Click on the gear symbol next to the name of the source.username.github.io repository (should result in the URL https://travis-ci.org/username/source.username.github.io/settings)&lt;/li&gt;
&lt;li&gt;Scroll down to Environment Variables - oh but maybe this last step isn't necessary because it is in the YAML file (travis.yml&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="travis-cli-with-docker-to-encrypt-the-oauth-token"&gt;Travis CLI with Docker to encrypt the OAuth Token&lt;/h3&gt;
&lt;p&gt;You do not want the unencrypted oauth token in your yaml file or even in the logs.  Instead leverage the handy feature of encrypted environment variables by encrypting your oauth token using the Travis CLI (ruby based so...)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is a special travis requirement of knowing the owner/repo , i.e. &lt;strong&gt;username/source.username.github.io&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The easy way&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-it&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;ruby:alpine&lt;span class="w"&gt; &lt;/span&gt;/bin/sh
apk&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;--no-cache&lt;span class="w"&gt; &lt;/span&gt;build-base
gem&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;travis&lt;span class="w"&gt; &lt;/span&gt;travis-lint
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The slightly longer way with Ubuntu&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-it&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;ubuntu:trusty
apt-get&lt;span class="w"&gt; &lt;/span&gt;update
apt-get&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;ruby1.9.3&lt;span class="w"&gt; &lt;/span&gt;build-essential
sudo&lt;span class="w"&gt; &lt;/span&gt;gem&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;travis&lt;span class="w"&gt; &lt;/span&gt;travis-lint
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The actual travis commands...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;travis&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;
travis&lt;span class="w"&gt; &lt;/span&gt;pubkey&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;username/source.username.github.io
travis&lt;span class="w"&gt; &lt;/span&gt;encrypt&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GH_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_github_personal_oauth_token&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;username/source.username.github.io
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Alternatively make a fake yaml file and get the exact output added by the commands:
- &lt;code&gt;touch .travis.yml&lt;/code&gt;
- &lt;code&gt;travis encrypt GH_TOKEN=your_github_personal_oauth_token -r username/source.username.github.io --add env.global&lt;/code&gt;
- &lt;code&gt;less .travis.yml&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.travis-ci.com/user/environment-variables/#Defining-encrypted-variables-in-.travis.yml"&gt;https://docs.travis-ci.com/user/environment-variables/#Defining-encrypted-variables-in-.travis.yml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.travis-ci.com/user/encryption-keys#Fetching-the-public-key-for-your-repository"&gt;https://docs.travis-ci.com/user/encryption-keys#Fetching-the-public-key-for-your-repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/travis-ci/travis.rb/issues/296"&gt;https://github.com/travis-ci/travis.rb/issues/296&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="travisyml"&gt;.travis.yml&lt;/h4&gt;
&lt;p&gt;At the root of your source.username.github.io you'll need the Travis configuration file (yaml)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;language:&lt;span class="w"&gt; &lt;/span&gt;python
python:
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.7"&lt;/span&gt;
before_install:
&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;update&lt;span class="w"&gt; &lt;/span&gt;-qq
install:
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;pelican&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.6.3&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Markdown&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.6.7&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;beautifulsoup4&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;.5.1
script:
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;./output
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;./cache
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;./plugins/*
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/getpelican/pelican-plugins.git
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;./pelican-plugins/*&lt;span class="w"&gt; &lt;/span&gt;./plugins
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;pelican&lt;span class="w"&gt; &lt;/span&gt;./content&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;./output&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;./publishconf.py
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;--quiet&lt;span class="w"&gt; &lt;/span&gt;https://&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GH_TOKEN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;@github.com/username/username.github.io.git&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/dev/null
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;johnpfeiffer.github.io
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;config&lt;span class="w"&gt; &lt;/span&gt;user.email&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"me@john-pfeiffer.com"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;config&lt;span class="w"&gt; &lt;/span&gt;user.name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Pfeiffer"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;rsync&lt;span class="w"&gt; &lt;/span&gt;-rv&lt;span class="w"&gt; &lt;/span&gt;--exclude&lt;span class="o"&gt;=&lt;/span&gt;.git&lt;span class="w"&gt; &lt;/span&gt;../output/*&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;commit&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Travis build &lt;/span&gt;&lt;span class="nv"&gt;$TRAVIS_BUILD_NUMBER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;push&lt;span class="w"&gt; &lt;/span&gt;-fq&lt;span class="w"&gt; &lt;/span&gt;origin&lt;span class="w"&gt; &lt;/span&gt;master&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/dev/null
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Done\n"&lt;/span&gt;
env:
&lt;span class="w"&gt;   &lt;/span&gt;global:
&lt;span class="w"&gt;     &lt;/span&gt;secure:&lt;span class="w"&gt; &lt;/span&gt;example126xOnLRCabGeZrxMUne9W0l5LTbN/hR5Wnq0P3nwrL4slWJ3rFAoi/wqivbINwZGOkU7e/OPVvjDCRivAIxeti61xtnKgyFL6rTvc7u5vAjCF6m4qx6+bXOx9YbXCEUdJmBd25qGBy3PIg4rt/524DOBZhZ9t4glt8Qo&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Use a python2.7 based travis builder with the pelican and its dependencies and the encrypted oauth token&lt;/li&gt;
&lt;li&gt;Also helpful: &lt;a href="https://lint.travis-ci.org"&gt;https://lint.travis-ci.org&lt;/a&gt; (validate .travis.yml) or &lt;code&gt;gem install travis-lint&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;The "target" repository is manually cloned using the encrypted oauth token, and the pelican output is then pushed to it.  No humans involved!&lt;/p&gt;
&lt;h2 id="using-a-cname-to-have-your-own-custom-domain-point-to-the-github-pages-pelican-blog"&gt;Using a CNAME to have your own custom domain point to the GitHub Pages Pelican Blog&lt;/h2&gt;
&lt;p&gt;To ensure maximum coolness (and SEO points) make sure you have DNS control over the domain you have in mind so you can redirect it to your new static pelican-based blog (hosted for free by github pages).&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Basically in the GitHub repo settings of source.username.github.io choose "Custom domain"&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then add a CNAME with your DNS provider (i.e. from Namecheap or Cloudflare I pointed blog.john-pfeiffer.com to johnpfeiffer.github.io)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://help.github.com/articles/using-a-custom-domain-with-github-pages/"&gt;https://help.github.com/articles/using-a-custom-domain-with-github-pages/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.github.com/articles/adding-or-removing-a-custom-domain-for-your-github-pages-site/"&gt;https://help.github.com/articles/adding-or-removing-a-custom-domain-for-your-github-pages-site/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.github.com/articles/setting-up-a-custom-subdomain/"&gt;https://help.github.com/articles/setting-up-a-custom-subdomain/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content><category term="build-CI-CD-devops"/><category term="static site"/><category term="pelican blog"/><category term="github pages"/><category term="travis ci"/></entry><entry><title>Mobile edit cloud execution of python code</title><link href="https://blog.john-pfeiffer.com/mobile-edit-cloud-execution-of-python-code/" rel="alternate"/><published>2014-09-07T21:37:00-07:00</published><updated>2014-09-07T21:37:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-09-07:/mobile-edit-cloud-execution-of-python-code/</id><summary type="html">&lt;p&gt;Haven't you just wanted to work through a coding kata &lt;a href="http://codekata.com"&gt;http://codekata.com&lt;/a&gt; or puzzle on your phone?&lt;/p&gt;
&lt;p&gt;Python is a great language for getting stuff done, and while there are some mobile apps often they are limited by the platform (eg ios sans file system).&lt;/p&gt;
&lt;p&gt;Using the link from …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Haven't you just wanted to work through a coding kata &lt;a href="http://codekata.com"&gt;http://codekata.com&lt;/a&gt; or puzzle on your phone?&lt;/p&gt;
&lt;p&gt;Python is a great language for getting stuff done, and while there are some mobile apps often they are limited by the platform (eg ios sans file system).&lt;/p&gt;
&lt;p&gt;Using the link from a Dropbox text file and a linode server (could be openshift red hat cloud?)...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;start running the script on the remote server that waits for new code to execute &lt;code&gt;python myflaskapp.py &amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;I can edit using Nocs &lt;/li&gt;
&lt;li&gt;Nocs syncs from iOS to Dropbox&lt;/li&gt;
&lt;li&gt;hitting a URL in my browser &lt;ul&gt;
&lt;li&gt;the python script downloads the latest version of the code (using shell to curl as Dropbox use javascript to authorize and return a link to the latest version) &lt;/li&gt;
&lt;li&gt;executes using the remote python environment &lt;/li&gt;
&lt;li&gt;returns the output&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note! This may be dangerous as hackers could exploit to run arbitrary code, use at your own risk.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;*Also, clearly, downloading from Dropbox using curl is a hack with no guarantee of future support =]&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;flask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;subprocess&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;urllib2&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/mysecreturl&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;update_and_run&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;exercises.py&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/var/www/mystuff&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;urlpath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://www.dropbox.com/s/ancdefgrandom/exercises.py?dl=0&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;curl --silent --location --insecure --output exercises.py https://www.dropbox.com/s/ancdefgrandom/exercises.py?dl=0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;python&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0.0.0.0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;use_reloader&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="flask-application-on-openshift"&gt;Flask Application on OpenShift&lt;/h3&gt;
&lt;p&gt;git clone https://github.com/openshift/flask-example.git&lt;/p&gt;
&lt;p&gt;Use the OpenShift WebUI to create an application&lt;/p&gt;
&lt;p&gt;On the right of your application the WebUI has a note on how to clone the default repo:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;ssh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="nv"&gt;@appname&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rhcloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/~/&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;appname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;appname&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;git&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;openshift&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;flask&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;recursive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;theirs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;
&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wsgi&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="n"&gt;virtualenv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;venv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;vi myflaskapp.py&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;flask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;subprocess&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;urllib2&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Hello World!&amp;quot;&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/mysecreturl&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;update_and_run&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/var/lib/openshift/12345appid/app-root/data/exercises.py&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;urlpath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://www.dropbox.com/s/12345random/exercises.py?dl=0&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;curl --silent --location --insecure --output &amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;urlpath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;python&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# output = Popen([&amp;quot;touch&amp;quot;,path], stdout=PIPE).communicate()[0]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="more-info"&gt;More Info&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.openshift.com/blogs/beginners-guide-to-writing-flask-apps-on-openshift"&gt;https://www.openshift.com/blogs/beginners-guide-to-writing-flask-apps-on-openshift&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="python"/><category term="flask"/><category term="openshift"/></entry><entry><title>Fix Fn screen brightness Ubuntu 14.04 intel graphics</title><link href="https://blog.john-pfeiffer.com/fix-fn-screen-brightness-ubuntu-1404-intel-graphics/" rel="alternate"/><published>2014-08-26T22:36:00-07:00</published><updated>2014-08-26T22:36:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-08-26:/fix-fn-screen-brightness-ubuntu-1404-intel-graphics/</id><summary type="html">&lt;p&gt;I discovered post upgrade that Ubuntu 14.04 has a glaring bug with the Intel graphics card (which was working fine in 12.04), the Fn key no longer controlled the brightness.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo su&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ls /sys/class/backlight&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;if it lists intel_backlight then this solution of adding the following should …&lt;/p&gt;&lt;/blockquote&gt;</summary><content type="html">&lt;p&gt;I discovered post upgrade that Ubuntu 14.04 has a glaring bug with the Intel graphics card (which was working fine in 12.04), the Fn key no longer controlled the brightness.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo su&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ls /sys/class/backlight&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;if it lists intel_backlight then this solution of adding the following should work for you too...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;vi /usr/share/X11/xorg.conf.d/20-intel.conf&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Section &amp;quot;Device&amp;quot;
    Identifier  &amp;quot;card0&amp;quot;
    Driver      &amp;quot;intel&amp;quot;
    Option      &amp;quot;Backlight&amp;quot;  &amp;quot;intel_backlight&amp;quot;
    BusID       &amp;quot;PCI:0:2:0&amp;quot;
EndSection
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Log out and log back in, function keys should now control the brightness again (no more glaring bug!)&lt;/p&gt;</content><category term="linux"/></entry><entry><title>Ubuntu Bootable USB, apt-get and dpkg, and the best packages to install</title><link href="https://blog.john-pfeiffer.com/ubuntu-bootable-usb-apt-get-and-dpkg-and-the-best-packages-to-install/" rel="alternate"/><published>2014-08-20T00:00:00-07:00</published><updated>2014-08-20T00:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-08-20:/ubuntu-bootable-usb-apt-get-and-dpkg-and-the-best-packages-to-install/</id><summary type="html">
&lt;p&gt;If you have a modern computer (BIOS) that can boot from USB it is well worth it since having the latest Ubuntu ISO on DVD tends to pile up extra plastic.&lt;/p&gt;
&lt;p&gt;After setting up the Operating System you will need to install some software (packages).&lt;/p&gt;
&lt;p&gt;And if you have an …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;If you have a modern computer (BIOS) that can boot from USB it is well worth it since having the latest Ubuntu ISO on DVD tends to pile up extra plastic.&lt;/p&gt;
&lt;p&gt;After setting up the Operating System you will need to install some software (packages).&lt;/p&gt;
&lt;p&gt;And if you have an SSD drive you will want to optimize your OS to not wear it out unnecessarily.&lt;/p&gt;
&lt;h2 id="write-an-iso-to-usb"&gt;Write an ISO to usb&lt;/h2&gt;
&lt;p&gt;Be very careful with &lt;strong&gt;sudo&lt;/strong&gt; or using the &lt;strong&gt;root&lt;/strong&gt; user as this can permanently remove files or render your operating system inoperable.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;dd is a low level command that writes bytes directly without any prompts&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;su&lt;/span&gt;
&lt;span class="nv"&gt;fdisk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;l&lt;/span&gt;
&lt;span class="nv"&gt;umount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;sdc1&lt;/span&gt;

&lt;span class="nv"&gt;dd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="nv"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;Desktop&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;.&lt;span class="mi"&gt;04&lt;/span&gt;.&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;amd64&lt;/span&gt;.&lt;span class="nv"&gt;iso&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;sdc&lt;/span&gt;

&lt;span class="mi"&gt;1171456&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;records&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;
&lt;span class="mi"&gt;1171456&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;records&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;out&lt;/span&gt;
&lt;span class="mi"&gt;599785472&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bytes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MB&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;copied&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;260&lt;/span&gt;.&lt;span class="mi"&gt;364&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;.&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MB&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;fdisk allows you to see the device (e.g. an 8GB usb stick)&lt;/p&gt;
&lt;p&gt;dd will overwrite from the "infile" to the "outfile" so make sure you get that target location correct!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="ubuntu-recovery-mode"&gt;Ubuntu Recovery mode&lt;/h2&gt;
&lt;p&gt;(which is access to a single root user command line)&lt;/p&gt;
&lt;p&gt;boot in recovery mode by using the arrow keys during boot (down to select Recovery)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;mount networking &lt;/li&gt;
&lt;li&gt;root shell&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mount -o rw,remount /&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;mount --all # might be needed too&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now you can fix grub or /etc/passwd or free up some hard drive space&lt;/p&gt;
&lt;h2 id="package-management-with-apt-commands"&gt;package management with apt commands&lt;/h2&gt;
&lt;p&gt;Debian has precompiled packages of binaries and libraries that can very easily be installed via the command line (or GUI) using Advanced Packaging Tool (APT) &lt;/p&gt;
&lt;p&gt;&lt;a href="https://wiki.debian.org/Apt"&gt;https://wiki.debian.org/Apt&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Since apt is a wonderful wrapper/manager of dpkg when you're in doubt most likely there is a dpkg command that will do what you need but it may take a lot of research and 8 parameters to do it ;]&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Advanced_Packaging_Tool"&gt;https://en.wikipedia.org/wiki/Advanced_Packaging_Tool&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hint: Ubuntu is based upon Debian&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="apt-cache"&gt;apt-cache&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-cache search ssh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;to find packages with the name ssh&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-cache search ssh | grep server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;if there are too many results pipe to grep to filter down the results&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;show&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ssh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;to show the details about a specific package&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-cache showpkg ssh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;to show more general info about a package&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-cache depends ssh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;to show the package dependencies&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="force-apt-to-use-ipv4-to-avoid-lengthy-ipv6-timeouts"&gt;Force Apt to use  IPv4 to avoid lengthy IPv6 timeouts&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;apt-get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Acquire&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;ForceIPv4&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;update&lt;/span&gt;
&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;apt-get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Acquire&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;ForceIPv4&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;vim&lt;/span&gt;
&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Acquire::ForceIPv4 "true";'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;d&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;99force-ipv4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;update, then install vim, then save the persistent config to always use ipv4&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="apt-get"&gt;apt-get&lt;/h3&gt;
&lt;h4 id="updating-package-indices-with-apt-get-update"&gt;updating package indices with apt-get update&lt;/h4&gt;
&lt;p&gt;Apt contains indices that need to be updated from the upstream repositories&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;/etc/apt/sources.list&lt;/strong&gt; is the main ubuntu repository listing&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;/etc/apt/sources.list.d&lt;/strong&gt; is the directory where additional apt repositories can be added (usually from ppa or 3rd party vendors)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.debian.org/mirror/mirrors_full"&gt;http://www.debian.org/mirror/mirrors_full&lt;/a&gt; for the Debian package mirror sites&lt;/li&gt;
&lt;li&gt;&lt;a href="http://packages.ubuntu.com/"&gt;http://packages.ubuntu.com/&lt;/a&gt; for a web ui based search of package details&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;apt-get clean&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;/var/cache/apt/archive folder keeps a copy of the downloaded .deb files
you will need an internet connection to download again any removed .deb files&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lists&lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;remove the indices in case they have gotten orphaned or corrupted, needs to be followed by apt-get update to repopulate&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-get update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;use /etc/apt/sources.list and /etc/apt/sources.list.d to update the package indices to determine if there are newer packages available&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;deb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;file:///file_store/archive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;trusty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;universe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;a snippet for how to configure apt to use a local repository (e.g. use reprepro to make a local mirror)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get update -o Dir::Etc::sourcelist="sources.list.d/example.list" -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;update only a single repository&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-cache dump
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;shows all installed packages&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To install netselect, a debian application that allows you to choose the "best" package mirror:
    sudo apt-get install netselect netselect-apt
    netselect-apt&lt;/p&gt;
&lt;h4 id="installing-and-force-installing-with-apt"&gt;installing and force installing with apt&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get install --dry-run byobu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;simulate what will happen but do not change the system&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;byobu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;packages are retrieved but not installed&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get install --yes byobu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;install and pre-emptively answer yes to the yes/no prompt&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get install --reinstall byobu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;reinstall even if the package is installed&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get install --fix-broken byobu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;attempt to fix broken dependencies&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;DEBIAN_FRONTEND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;noninteractive&lt;/span&gt; &lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="n"&gt;Dpkg&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"--force-confdef"&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="n"&gt;Dpkg&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"--force-confold"&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;reinstall&lt;/span&gt; &lt;span class="n"&gt;byobu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the most non interactive way to force install a package where all prompts are auto answered such that old configuration files are maintained&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://manpages.ubuntu.com/manpages/precise/man8/apt-get.8.html"&gt;http://manpages.ubuntu.com/manpages/precise/man8/apt-get.8.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://help.ubuntu.com/community/AptGet/Howto"&gt;https://help.ubuntu.com/community/AptGet/Howto&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;apt-get upgrade&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;upgrades to the latest version of existing packages, no new packages (so if the new version has new dependencies nothing happens)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;apt-get dist-upgrade&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;upgrades to the latest version of existing packages and will try to grab any new dependencies as required&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;apt-get install update-manager-core&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;newer versions of ubuntu require a helper utility, &lt;a href="http://packages.ubuntu.com/trusty/admin/update-manager-core"&gt;http://packages.ubuntu.com/trusty/admin/update-manager-core&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before you do a major upgrade of Ubuntu you should bring all packages to the latest version... (apt-get update &amp;amp;&amp;amp; apt-get dist-upgrade)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;release&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;upgrade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DistUpgradeViewNonInteractive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;non interactive upgrade to a new version of Ubuntu (hold onto your seat!), often requires a reboot after for kernel upgrades&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;lsb_release -a
cat /etc/lsb_release
uname -a
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;verify that your system has been upgraded (kernel upgrades often require a reboot to become loaded in memory))&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="removing-packages-with-apt"&gt;removing packages with apt&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-get remove wget
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;uninstall a package&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-get purge wget
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;remove the package and all files from disk&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-get autoremove
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;attempt to clean up packages that are no longer needed (i.e. old versions of dependencies or unused kernel images)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="apt-key"&gt;apt-key&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-key update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;if apt errors: WARNING: The following packages cannot be authenticated&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="dpkg-really-manages-everything"&gt;dpkg really manages everything&lt;/h2&gt;
&lt;p&gt;Underneath apt is dpkg (and similar tools) which actually does all of the hard work but are sometimes hard to use =)&lt;/p&gt;
&lt;h3 id="listing-and-finding-packages-with-dpkg"&gt;listing and finding packages with dpkg&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;dpkg -l
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;lists all of the packages installed (name, version, architecture, description)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;dpkg -l | grep foobar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;lists all of the packages but filters for something specific (i.e. a prefix or partial match)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;dpkg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packagename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;myoutput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;lists whether a specific package is installed or not and redirects the output to a file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;dpkg --get-selections
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;lists the package names and the state (installed, uninstalled, etc.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;dpkg-query -f '${binary:Package}\n' -W&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;lists just the package names, slightly more convenient is &lt;code&gt;apt-cache pkgnames | sort&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;dpkg -S stdio.h
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find a package that contains a specific file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;dpkg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packagename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;list the contents of the .deb file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://wiki.debian.org/ListInstalledPackages"&gt;https://wiki.debian.org/ListInstalledPackages&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;you can also manually inspect /var/lib/apt and /var/lib/dpkg&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="dpkg-logs"&gt;dpkg logs&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;vi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dpkg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dpkg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;in conjunction with &lt;code&gt;apt-get upgrade -y&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="installing-and-removing-packages-with-dpkg"&gt;Installing and removing packages with dpkg&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;dpkg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packagename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;install the .deb file, &lt;code&gt;dpkg -i *.deb&lt;/code&gt; will install all of the .deb files in the current directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;dpkg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;depends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packagename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;installs and turns a dependency error into a warning (i.e. libc6 circular dependency)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;dpkg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packagename&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;list the locations of the installed files&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;dpkg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packagename&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;shows if the package is installed and information about it, &lt;code&gt;dpkg -s | grep Version&lt;/code&gt;
or &lt;code&gt;dpkg -l | awk '$2=="packagename" { print $3 }'&lt;/code&gt; to only print the version (if it exists)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;dpkg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packagename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;remove a package but leave the configuration files, also known as &lt;code&gt;dpkg --remove&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;dpkg --purge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;remove a package and delete all configuration files (even if they have been customized by the user)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;dpkg --force-help
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="to-manually-install-a-package-forcefully-if-synaptic-and-apt-get-are-stuck"&gt;to manually install a package (forcefully if synaptic and apt-get are stuck)&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dpkg&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;postgresql&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="n"&gt;dpkg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;force&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;reinstreq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;postgresql&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;9.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;do the same for postgresql-common and other packages&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;apt-get install postgresql-9.1
apt-get purge postgresql-9.1 postgresql-client-9.1 postgresql-common postgresql-client-common&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;in order to have apt-get remove all of the binaries&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="best-ubuntu-packages"&gt;Best Ubuntu Packages&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;as of Utopic 14.10&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get update
sudo apt-get install -y byobu build-essential elinks unzip unrar vim wget curl ntp rcconf dialog git-core 
sudo apt-get install -y python-pip &amp;amp;&amp;amp; sudo pip install --upgrade pip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;pip is the package manager for python packages (different from the debian OS packages) so useful if you do any python development or run python applications
An alternative to the usually stale OS pip version is to use the not entirely secure grab the .py file from the internet and run it...
wget -qO- https://bootstrap.pypa.io/get-pip.py | sudo python&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;openssh-server libssl = the secure remote shell service and encryption dependency &lt;a href="http://packages.ubuntu.com/search?keywords=openssh-server"&gt;http://packages.ubuntu.com/search?keywords=openssh-server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;build-essential = tools for compiling and building debian packages &lt;a href="http://packages.ubuntu.com/lucid/build-essential"&gt;http://packages.ubuntu.com/lucid/build-essential&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;byobu = console terminal multi screen (survives network disconnects) &lt;a href="http://byobu.co"&gt;http://byobu.co&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;wget and curl = utilities to download files over the network&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;elinks = cli browser (just in case your GUI dies and you need to research) &lt;a href="http://kmandla.wordpress.com/2011/01/13/a-comparison-of-text-based-browsers"&gt;http://kmandla.wordpress.com/2011/01/13/a-comparison-of-text-based-browsers&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;unzip and unrar = utilities to decompress compressed things&lt;/li&gt;
&lt;li&gt;ntp = network time protocol client daemon to keep your clock in sync&lt;/li&gt;
&lt;li&gt;rcconf = easier way to manage what services start at boot &lt;a href="https://packages.debian.org/jessie/rcconf"&gt;https://packages.debian.org/jessie/rcconf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;dialog = user friendly dialog boxes for shell scripts (dependency for rcconf)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git-core = the distributed version control software that is eating the developer world&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;python-setuptools = Sometimes required to install pip  &lt;a href="http://pythonhosted.org/setuptools"&gt;http://pythonhosted.org/setuptools&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;nano = a simple text editor (much easier than vi/vim for just writing new text)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;sudo apt-get install openconnect network-manager-openconnect network-manager-openconnect-gnome&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;openconnect = opensource compatible with cisco anyconnect vpn &lt;a href="https://en.wikipedia.org/wiki/OpenConnect"&gt;https://en.wikipedia.org/wiki/OpenConnect&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;openvpn = opensource vpn client &lt;a href="https://openvpn.net"&gt;https://openvpn.net&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;sudo apt-get install openjdk-8-jre&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;for just the java runtime (thank goodness not Oracle Sun)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;jdk = java development kit&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;iced-tea-7-plugin = open source java 7 support for browsers&lt;/li&gt;
&lt;li&gt;icedtea = open java (plugin = browser java)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;openjdk-8-jdk&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;for the full java development kit - needed for some packages to run correctly&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="gui"&gt;GUI&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Xubuntu Desktop&lt;/strong&gt; is my preferred "lightweight" GUI for Ubuntu: &lt;a href="http://xubuntu.org"&gt;http://xubuntu.org&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-get install -y chromium-browser pepperflashplugin-nonfree geany keepassx xdiskusage
apt-get install -y arandr rdesktop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;chromium-browser = opensource branch/clone of google chrome browser, &lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;maybe srware.net with privacy badger and adblock plus (fanboy block lists) too?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;geany = tabbed text notepad (with syntax highlighting)&lt;/li&gt;
&lt;li&gt;keepassx = secure password inventory (has a mini version for iphone as well)&lt;/li&gt;
&lt;li&gt;xdiskusage = graphical view of disk space usage by folder and file&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;arandr = multi display gui config&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;rdesktop = RDP client&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;grdesktop = gnome UI for rdesktop&lt;/p&gt;
&lt;p&gt;sudo echo "autologin-user=ubuntu" &amp;gt;&amp;gt;  /etc/lightdm/lightdm.conf.d/10-xubuntu.conf&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Better yet use the UI and just choose auto login ;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;TODO: &lt;em&gt;Disable guest user&lt;/em&gt;, *Disable crash reports: apport *&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;imagemagick = image transformation utility &lt;a href="https://en.wikipedia.org/wiki/ImageMagick"&gt;https://en.wikipedia.org/wiki/ImageMagick&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;convert -resize 50% source.png dest.jpg ; convert -rotate 90 source.jpg dest.jpg&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;dropbox = cloud file storage&lt;/p&gt;
&lt;p&gt;deb http://linux.dropbox.com/ubuntu utopic main
sudo apt-key adv --keyserver pgp.mit.edu --recv-keys 5044912E&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;deb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;http://downloads.hipchat.com/linux/apt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;stable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;main&lt;/span&gt;
&lt;span class="err"&gt;wget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-O&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;https://www.hipchat.com/keys/hipchat-linux.key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;apt-key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;apt-get update; apt-get install dropbox hipchat&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;filezilla = file transfer protocol client (that supports sftp = secure ssh ftp)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="music-and-video"&gt;music and video&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get install ubuntu-restricted-extras vlc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;vlc = movies/music&lt;/li&gt;
&lt;li&gt;ubuntu-restricted-extras = all of the encumbered with licenses packages to generally just watch or listen to stuff :(&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="google-music-manager"&gt;Google Music Manager&lt;/h4&gt;
&lt;p&gt;If you are you using the web based &lt;a href="https://play.google.com/music/listen#/all"&gt;https://play.google.com/music/listen#/all&lt;/a&gt; then you probably want the uploader/downloader:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;wget https://dl.google.com/linux/direct/google-musicmanager-beta_current_amd64.deb
dpkg -i google-musicmanager-beta_current_amd64.deb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="spotify-in-ubuntu-1504"&gt;spotify in ubuntu 15.04&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://www.spotify.com/us/download/linux/"&gt;https://www.spotify.com/us/download/linux/&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys D2C19886
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;trust the spotify repository&lt;/p&gt;
&lt;p&gt;libnss3-1d : Depends: libnss3 (= 2:3.17.4-0ubuntu1) but 2:3.19.2-0ubuntu15.04.1 is to be installed
spotify is behind the times or only wants to support 14.04 and LTS releases =(&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://launchpad.net/ubuntu/vivid/amd64/libnss3/2:3.17.4-0ubuntu1"&gt;https://launchpad.net/ubuntu/vivid/amd64/libnss3/2:3.17.4-0ubuntu1&lt;/a&gt;
&lt;a href="https://launchpad.net/ubuntu/wily/amd64/libnss3/2:3.19.2.1-0ubuntu0.15.10.1"&gt;https://launchpad.net/ubuntu/wily/amd64/libnss3/2:3.19.2.1-0ubuntu0.15.10.1&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;dpkg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;libnss3_3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;17.4&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;ubuntu1_amd64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deb&lt;/span&gt;
&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;spotify&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;
&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;

&lt;span class="n"&gt;spotify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;libraries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;libgcrypt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;so&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cannot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;such&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;what fun, the internet explains 15.04 (vivid) and 15.10 (wily) use the new libgcrypt20 so...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;wget https://launchpad.net/ubuntu/+archive/primary/+files/libgcrypt11_1.5.3-2ubuntu4.2_amd64.deb
dpkg -i libgcrypt*.deb
apt-get install --reinstall spotify-client
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="pithos-is-an-open-source-pandora-client"&gt;pithos is an open source pandora client&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sudo add-apt-repository ppa:pithos/ppa&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo apt-get install pithos&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="more-codecs-and-dvd-playback"&gt;more codecs and DVD playback&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get install ffmpeg gstreamer0.10-plugins-bad lame libavcodec-extra
sudo /usr/share/doc/libdvdread4/install-css.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="packages-you-will-probably-want-to-remove"&gt;Packages you will probably want to remove&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-get remove brltty
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;unless you are using braille on your computer&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="other-useful-packages"&gt;Other Useful Packages&lt;/h2&gt;
&lt;h3 id="heroku-cli"&gt;Heroku CLI&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;deb http://toolbelt.heroku.com/ubuntu ./&lt;/li&gt;
&lt;li&gt;wget -O- https://toolbelt.heroku.com/apt/release.key | apt-key add -&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apt-get install -y heroku-toolbelt&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ruby-and-openshift-cli"&gt;Ruby and OpenShift CLI&lt;/h3&gt;
&lt;p&gt;https://gorails.com/setup/ubuntu/14.04&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 \&lt;/code&gt;
&lt;code&gt;libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libgdbm-dev libncurses5-dev automake libtool bison libffi-dev&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;source ~/.rvm/scripts/rvm
echo "source ~/.rvm/scripts/rvm" &amp;gt;&amp;gt; ~/.bashrc
rvm install 2.1.2
rvm use 2.1.2 --default
ruby -v
echo "gem: --no-ri --no-rdoc" &amp;gt; ~/.gemrc

gem install rhc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;red hat client for openshift&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="ssd-optimization"&gt;SSD Optimization&lt;/h2&gt;
&lt;h3 id="write-logs-to-tmpfs-instead-of-disk"&gt;Write Logs to tmpfs instead of disk&lt;/h3&gt;
&lt;p&gt;tmpfs ram (memory) virtual disk will just use memory (which I guess is overly abundant now) instead of wearing out the Solid State Drive&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;fstab&lt;/span&gt;


&lt;span class="c1"&gt;# /etc/fstab: static file system information.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Use 'blkid' to print the universally unique identifier for a&lt;/span&gt;
&lt;span class="c1"&gt;# device; this may be used with UUID= as a more robust way to name devices&lt;/span&gt;
&lt;span class="c1"&gt;# that works even if disks are added and removed. See fstab(5).&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;lt;file system&amp;gt; &amp;lt;mount point&amp;gt;   &amp;lt;type&amp;gt;  &amp;lt;options&amp;gt;       &amp;lt;dump&amp;gt;  &amp;lt;pass&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;# / was on /dev/sda2 during installation&lt;/span&gt;
&lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;b7577587&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="n"&gt;f6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;264&lt;/span&gt;&lt;span class="n"&gt;a24f9dd90&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="n"&gt;ext4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;remount&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;apparmor&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cups&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;upgrade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;installer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lightdm&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;unattended&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;upgrades&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the tmpfs disks created in my fstab were discovered through trial and error and will differ based on what applications are actually running (Xubuntu!)&lt;/p&gt;
&lt;p&gt;an older simpler example causes errors as applications create /var/log/SOMETHING directories during installation and then expect them on boot every time later&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sda1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;ext4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;remount&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ro&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;tmpfs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;noatime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;here is a list of directories that probably need to be generated
&lt;em&gt;for dir in apparmor apt cups dist-upgrade fsck gdm installer news samba unattended-upgrades ; do&lt;/em&gt;
*  mkdir -p /var/log/$dir&lt;em&gt;
&lt;/em&gt;done*&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="customize-grub-boot-options"&gt;Customize Grub Boot Options&lt;/h3&gt;
&lt;p&gt;I prefer seeing my bootup screens so I remove some but add the SSD enhancement&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vi nano /etc/default/grub
    GRUB_TIMEOUT=1
    #   GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
    GRUB_CMDLINE_LINUX_DEFAULT="elevator=noop"

sudo update-grub2
cat /boot/grub/grub.cfg
    root=UUID=f0ae2c59-83d2-42e7-81c4-2e870b6b255d ro quiet splash elevator=noop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;only prompt for 1 second
remove the quiet so the console displays all of the boot information
the noop scheduler is a simple FIFO scheduler which is usually optimal for SSD or virtual machines &lt;a href="https://en.wikipedia.org/wiki/Noop_scheduler"&gt;https://en.wikipedia.org/wiki/Noop_scheduler&lt;/a&gt; since any OS attempt at optimization may cnoflict with more accurate information from the Disk or Hypervisor
update-grub2 is to apply the update &lt;a href="https://help.ubuntu.com/community/Grub2"&gt;https://help.ubuntu.com/community/Grub2&lt;/a&gt;
manually verify the changes by examining all of the boot menu options (i.e. find the noop line)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sda&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;scheduler&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;noop&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deadline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cfq&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;list what schedulers are available , &lt;a href="http://www.linuxhowtos.org/System/iosched.htm"&gt;http://www.linuxhowtos.org/System/iosched.htm&lt;/a&gt;, note that noop is selected&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: the above command needs to be run as root, but sudo does not work with it on my system. Run sudo -i if you have a problem to get a root prompt.)&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="good-digital-ocean-droplet-tips"&gt;Good Digital Ocean Droplet Tips&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;useradd -s /bin/bash -m NEWUSERNAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;usermod -a -G admin NEWUSERNAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;passwd NEWUSERNAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;mkdir -p /home/NEWUSERNAME/.ssh&lt;/li&gt;
&lt;li&gt;vi /home/NEWUSERNAME/.ssh/authorized_keys&lt;/li&gt;
&lt;li&gt;visudo
    &lt;code&gt;#includedir /etc/sudoers.d&lt;/code&gt;
&lt;code&gt;NEWUSERNAME    ALL = (ALL) NOPASSWD: ALL&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;of course set the timezone to UTC and use network time protocol&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;dpkg-reconfigure tzdata&lt;/li&gt;
&lt;li&gt;apt-get update&lt;/li&gt;
&lt;li&gt;apt-get install byobu vim ntp&lt;/li&gt;
&lt;li&gt;Secure SSH by removing root login with vi /etc/ssh/sshd_config: &lt;code&gt;PermitRootLogin no&lt;/code&gt; , &lt;code&gt;PasswordAuthentication no&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Optionally change the SSH port to something different from the default: &lt;code&gt;Port 22&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;/etc/init.d/ssh restart&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Use the Digital Ocean Web UI to poweroff and take a snapshot of the fresh system&lt;/p&gt;
&lt;h1 id="revisting-apt-tips"&gt;Revisting Apt Tips&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;apt list --installed | grep packagename&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;list what is installed but filter for just one name&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;apt-get update&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;refresh from the upstream sources what might be available for install or upgrade&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;apt install --only-upgrade packagename&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;upgrade one specific package&lt;/p&gt;
&lt;/blockquote&gt;</content><category term="linux"/><category term="linux"/><category term="ubuntu"/><category term="xubuntu"/><category term="usb boot"/><category term="ubuntu recovery"/><category term="dpkg"/><category term="apt-get"/><category term="dd"/></entry><entry><title>ListJS: Sort, Filters, Search and more for HTML lists and tables in Javascript</title><link href="https://blog.john-pfeiffer.com/listjs-sort-filters-search-and-more-for-html-lists-and-tables-in-javascript/" rel="alternate"/><published>2014-08-08T21:37:00-07:00</published><updated>2014-08-08T21:37:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-08-08:/listjs-sort-filters-search-and-more-for-html-lists-and-tables-in-javascript/</id><summary type="html">
&lt;p&gt;Self contained javascript library to make lists of information awesome!&lt;/p&gt;
&lt;p&gt;&lt;a href="http://listjs.com/docs"&gt;http://listjs.com/docs&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="listjshtml"&gt;listjs.html&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="s s-Atom"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;DOCTYPE&lt;/span&gt; &lt;span class="s s-Atom"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;script&lt;/span&gt; &lt;span class="s s-Atom"&gt;src=&lt;/span&gt;&lt;span class="s2"&gt;"http://listjs.com/no-cdn/list.js"&lt;/span&gt;&lt;span class="s s-Atom"&gt;&amp;gt;&amp;lt;/script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;style&lt;/span&gt; &lt;span class="s s-Atom"&gt;type=&lt;/span&gt;&lt;span class="s2"&gt;"text/css"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;sans&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;list&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s s-Atom"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;background …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">
&lt;p&gt;Self contained javascript library to make lists of information awesome!&lt;/p&gt;
&lt;p&gt;&lt;a href="http://listjs.com/docs"&gt;http://listjs.com/docs&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="listjshtml"&gt;listjs.html&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="s s-Atom"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;DOCTYPE&lt;/span&gt; &lt;span class="s s-Atom"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;script&lt;/span&gt; &lt;span class="s s-Atom"&gt;src=&lt;/span&gt;&lt;span class="s2"&gt;"http://listjs.com/no-cdn/list.js"&lt;/span&gt;&lt;span class="s s-Atom"&gt;&amp;gt;&amp;lt;/script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;style&lt;/span&gt; &lt;span class="s s-Atom"&gt;type=&lt;/span&gt;&lt;span class="s2"&gt;"text/css"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;sans&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;list&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s s-Atom"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;background&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s s-Atom"&gt;#eee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;box&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s s-Atom"&gt;inset&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="s s-Atom"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;avatar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;max&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s s-Atom"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;max&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="c1"&gt;%;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s s-Atom"&gt;h3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="s s-Atom"&gt;rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s s-Atom"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s s-Atom"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="s s-Atom"&gt;input&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;solid&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="s s-Atom"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s s-Atom"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;focus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;#aaa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;sort&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;inline&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;text&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;background&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s s-Atom"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="s s-Atom"&gt;a8e0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;text&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;background&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s s-Atom"&gt;b8aba&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;focus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="s s-Atom"&gt;solid&lt;/span&gt; &lt;span class="s s-Atom"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="s s-Atom"&gt;solid&lt;/span&gt; &lt;span class="s s-Atom"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="s s-Atom"&gt;solid&lt;/span&gt; &lt;span class="s s-Atom"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s s-Atom"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;asc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="s s-Atom"&gt;solid&lt;/span&gt; &lt;span class="s s-Atom"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="s s-Atom"&gt;solid&lt;/span&gt; &lt;span class="s s-Atom"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="s s-Atom"&gt;solid&lt;/span&gt; &lt;span class="s s-Atom"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s s-Atom"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s s-Atom"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s s-Atom"&gt;after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="s s-Atom"&gt;solid&lt;/span&gt; &lt;span class="s s-Atom"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="s s-Atom"&gt;solid&lt;/span&gt; &lt;span class="s s-Atom"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s s-Atom"&gt;px&lt;/span&gt; &lt;span class="s s-Atom"&gt;solid&lt;/span&gt; &lt;span class="s s-Atom"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="s s-Atom"&gt;&amp;lt;/style&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;



&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;meta&lt;/span&gt; &lt;span class="s s-Atom"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s s-Atom"&gt;utf&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="s s-Atom"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;Existing&lt;/span&gt; &lt;span class="s s-Atom"&gt;list&amp;lt;/title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s s-Atom"&gt;&amp;lt;/head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="s s-Atom"&gt;id=&lt;/span&gt;&lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;input&lt;/span&gt; &lt;span class="s s-Atom"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"search"&lt;/span&gt; &lt;span class="s s-Atom"&gt;placeholder=&lt;/span&gt;&lt;span class="s2"&gt;"Search"&lt;/span&gt; &lt;span class="s s-Atom"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;button&lt;/span&gt; &lt;span class="s s-Atom"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"sort"&lt;/span&gt; &lt;span class="s s-Atom"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="s s-Atom"&gt;sort=&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nv"&gt;Sort&lt;/span&gt; &lt;span class="s s-Atom"&gt;by&lt;/span&gt; &lt;span class="s s-Atom"&gt;name&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;&amp;lt;/button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;ul&lt;/span&gt; &lt;span class="s s-Atom"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"list"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;h3&lt;/span&gt; &lt;span class="s s-Atom"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;Jonny&lt;/span&gt; &lt;span class="nv"&gt;Stromberg&lt;/span&gt;&lt;span class="s s-Atom"&gt;&amp;lt;/h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;p&lt;/span&gt; &lt;span class="s s-Atom"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"born"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1986&lt;/span&gt;&lt;span class="s s-Atom"&gt;&amp;lt;/p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s s-Atom"&gt;&amp;lt;/li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;h3&lt;/span&gt; &lt;span class="s s-Atom"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;Jonas&lt;/span&gt; &lt;span class="nv"&gt;Arnklint&lt;/span&gt;&lt;span class="s s-Atom"&gt;&amp;lt;/h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;p&lt;/span&gt; &lt;span class="s s-Atom"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"born"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1985&lt;/span&gt;&lt;span class="s s-Atom"&gt;&amp;lt;/p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s s-Atom"&gt;&amp;lt;/li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;h3&lt;/span&gt; &lt;span class="s s-Atom"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;Martina&lt;/span&gt; &lt;span class="nv"&gt;Elm&lt;/span&gt;&lt;span class="s s-Atom"&gt;&amp;lt;/h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;p&lt;/span&gt; &lt;span class="s s-Atom"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"born"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1986&lt;/span&gt;&lt;span class="s s-Atom"&gt;&amp;lt;/p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s s-Atom"&gt;&amp;lt;/li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;h3&lt;/span&gt; &lt;span class="s s-Atom"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;Gustaf&lt;/span&gt; &lt;span class="nv"&gt;Lindqvist&lt;/span&gt;&lt;span class="s s-Atom"&gt;&amp;lt;/h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;p&lt;/span&gt; &lt;span class="s s-Atom"&gt;class=&lt;/span&gt;&lt;span class="s2"&gt;"born"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1983&lt;/span&gt;&lt;span class="s s-Atom"&gt;&amp;lt;/p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s s-Atom"&gt;&amp;lt;/li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s s-Atom"&gt;&amp;lt;/ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="s s-Atom"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="o"&gt;div&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="s s-Atom"&gt;script&lt;/span&gt; &lt;span class="s s-Atom"&gt;type=&lt;/span&gt;&lt;span class="s2"&gt;"text/javascript"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s s-Atom"&gt;var&lt;/span&gt; &lt;span class="s s-Atom"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;val&lt;/span&gt;&lt;span class="s s-Atom"&gt;ueNames&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s s-Atom"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;'born'&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="s s-Atom"&gt;var&lt;/span&gt; &lt;span class="nb"&gt;user&lt;/span&gt;&lt;span class="nv"&gt;List&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s s-Atom"&gt;new&lt;/span&gt; &lt;span class="nv"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="s s-Atom"&gt;&amp;lt;/script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="s s-Atom"&gt;&amp;lt;/body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s s-Atom"&gt;&amp;lt;/html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="listjs-with-pelican-and-jinja2"&gt;ListJS with Pelican and Jinja2&lt;/h3&gt;
&lt;p&gt;ListJS with the Pelican elegant theme to list all the articles sortable/searchable, default pagination for ListJS set to 1000 items&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{% extends "base.html" %}

{% block title %}
All Categories · {{ super() }}
{% endblock title %}

{% block head_description %}
All categories of the {{ SITENAME|striptags }} blog. 
{% endblock head_description %}
{% block content %}


&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://listjs.com/no-cdn/list.js"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/css"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="kt"&gt;rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;focus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mh"&gt;#aaa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;sort&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;#28a8e0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;hover&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mh"&gt;#1b8aba&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;focus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;-10&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;-5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;asc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;-5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"article-list"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"sort"&lt;/span&gt; &lt;span class="na"&gt;data-sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sort by date&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"sort"&lt;/span&gt; &lt;span class="na"&gt;data-sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sort by title&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"margin-top: 10px; height: 16px;"&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"list"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    {% for category, articles in categories %}
        {% for article in articles %}
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"date"&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"padding-right: 10px;"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;time&lt;/span&gt; &lt;span class="na"&gt;pubdate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"pubdate"&lt;/span&gt; &lt;span class="na"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"{{ article.date.isoformat() }}"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ article.locale_date }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;time&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"{{ SITEURL }}/{{ article.url }}"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ article.title }} {%if article.subtitle %}
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; {{ article.subtitle }} &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; {% endif %}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        {% endfor %}
    {% endfor %}
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


{% endblock content %}
{% block script %}
{{ super() }}
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;  &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"javascript"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;uncollapse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;collapse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"JavaScript"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;uncollapse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;valueNames&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1000&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hackerList&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'article-list'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;hackerList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'date'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

{% endblock script %}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="programming"/><category term="programming"/><category term="javascript"/></entry><entry><title>Intro to Amazon AWS Elastic Beanstalk</title><link href="https://blog.john-pfeiffer.com/intro-to-amazon-aws-elastic-beanstalk/" rel="alternate"/><published>2014-08-08T20:00:00-07:00</published><updated>2014-08-08T20:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-08-08:/intro-to-amazon-aws-elastic-beanstalk/</id><summary type="html">
&lt;p&gt;While InfrastructureAsAService lightens the load of Operations, to truly push forward Developers require a frictionless place to deploy applications.&lt;/p&gt;
&lt;p&gt;Platform-as-a-Service fills this niche: for this specific example AWS Elastic Beanstalk attempts to create a layer of abstraction on top of existing Amazon technologies. &lt;em&gt;(updated in 2019)&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PaaS = no OS management …&lt;/p&gt;&lt;/blockquote&gt;</summary><content type="html">
&lt;p&gt;While InfrastructureAsAService lightens the load of Operations, to truly push forward Developers require a frictionless place to deploy applications.&lt;/p&gt;
&lt;p&gt;Platform-as-a-Service fills this niche: for this specific example AWS Elastic Beanstalk attempts to create a layer of abstraction on top of existing Amazon technologies. &lt;em&gt;(updated in 2019)&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PaaS = no OS management, no ssh required, no chef/puppet scripting, potentially easier (dynamic) scaling&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;A previous article about a Google PaaS &lt;a href="https://blog.john-pfeiffer.com/google-app-engine-python"&gt;https://blog.john-pfeiffer.com/google-app-engine-python&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="installing-dependencies-to-use-the-cli"&gt;Installing dependencies (to use the CLI)&lt;/h2&gt;
&lt;p&gt;The AWS CLI relies heavily on Python.  &lt;em&gt;Originally this guide was written for python2 but now the world is all python3.&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;which pip3
pip3 --version
sudo apt install python3-pip --reinstall
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;(to fix when it is missing in ubuntu)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;pip3 install --upgrade awscli&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;make sure to be on the latest AWS CLI&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;pip install awsebcli --upgrade&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This might upgrade libraries from the OS that you prefer not to, so you could use --user&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/Welcome.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/Welcome.html&lt;/a&gt; to ElasticBeanstalk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3-install-advanced.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3-install-advanced.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;export PATH=$PATH:~/.local/bin&lt;/li&gt;
&lt;li&gt;Optionally add this to ~/.bashrc , e.g. &lt;code&gt;echo "export PATH=$PATH:~/.local/bin" &amp;gt;&amp;gt; ~/.bashrc&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;hopefully &lt;a href="https://github.com/aws/aws-elastic-beanstalk-cli-setup"&gt;https://github.com/aws/aws-elastic-beanstalk-cli-setup&lt;/a&gt; listing all the dependencies can help if there are any issues&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(deprecated) Read about how to use the CLI &lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-api-cli.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-api-cli.html&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="aws-account-credentials-to-generate-a-cli-security-file"&gt;AWS account credentials to generate a CLI security file&lt;/h2&gt;
&lt;p&gt;There is the usual best practice of creating an IAM user &lt;em&gt;(e.g. ebdemo)&lt;/em&gt; with a group that has the "AWSElasticBeanstalkFullAccess" attached policy...&lt;/p&gt;
&lt;p&gt;And with those credentials...&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vim ~/.ssh/awscli&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;AWSAccessKeyId=Write your AWS access ID
AWSSecretKey=Write your AWS secret key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;chmod 600 ~/.ssh/awscli&lt;/code&gt;
&lt;code&gt;export AWS_CREDENTIAL_FILE=~/.ssh/awscli&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Or alternatively use the usual AWS CLI: &lt;code&gt;aws configure&lt;/code&gt; &lt;em&gt;which puts things in ~/.aws/credentials&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="initial-directory-and-dependency-setup"&gt;Initial directory and dependency setup&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir ebjohnexample
cd ebjohnexample
virtualenv venv --python python3.6
source venv/bin/activate
pip install flask==1.1.1
pip freeze &amp;gt; requirements.txt
vim application.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="a-tiny-flask-python-webapp"&gt;A tiny flask python webapp&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;application.py&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;

&lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"hi"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;python application.py&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Serving&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lazy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;production&lt;/span&gt;
&lt;span class="n"&gt;WARNING&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;development&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;production&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deployment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;production&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;WSGI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instead&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;
&lt;span class="n"&gt;Running&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mf"&gt;127.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CTRL&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;curl 127.0.0.1:5000&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/Flask/"&gt;https://pypi.org/project/Flask/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="creating-an-elastic-beanstalk-project"&gt;Creating an Elastic Beanstalk Project&lt;/h2&gt;
&lt;p&gt;While Java and .jar files are probably the obvious example I preferred to do this in python =)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The pricing/charges are "only" for the underlying resources, so at first an EC2 instance plus a load balancer...&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;echo "venv" &amp;gt; .ebignore ; cat .ebignore&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ensure elastic beanstalk ignores the local dev dependencies&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;eb init -p python-3.6 ebjohnexample --region us-east-1&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;creates a python3.6 elastic beanstalk application named "ebjohnexample"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Ignoring setting up SSH since we really ought to never SSH into application servers , 12factor and all that&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb3-init.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb3-init.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;eb create development&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This command can take around 5 minutes to create all the resources&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Creating&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"app-191020_203345"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Uploading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ebjohnexample&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;191020&lt;/span&gt;&lt;span class="n"&gt;_203345&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;S3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;may&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Upload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Complete&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;development&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ebjohnexample&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;us&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;east&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Deployed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;191020&lt;/span&gt;&lt;span class="n"&gt;_203345&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zqag8vpi2p&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;elasticbeanstalk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;us&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;east&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="n"&gt;bit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Amazon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Linux&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;2.9&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Tier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;WebServer&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Standard&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;CNAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Updated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;48.839000&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Printing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;createEnvironment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;starting&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;elasticbeanstalk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;us&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;east&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;538676797434&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Amazon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;S3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;load&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balancer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;awseb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AWSEBLoa&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;LEU58ECO2D9O&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;awseb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zqag8vpi2p&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AWSEBSecurityGroup&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;HFU5QRINE35V&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Scaling&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;launch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;awseb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zqag8vpi2p&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AWSEBAutoScalingLaunchConfiguration&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;WRM6IN89BH24&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Scaling&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;awseb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zqag8vpi2p&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AWSEBAutoScalingGroup&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;LS20VBXQLOU&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Waiting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EC2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instances&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;launch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;may&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;few&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Scaling&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;autoscaling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;us&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;east&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;538676797434&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;scalingPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;a0d4a40a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5060&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;406&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;b3df&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="n"&gt;ed43f8834d1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;autoScalingGroupName&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;awseb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zqag8vpi2p&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AWSEBAutoScalingGroup&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;LS20VBXQLOU&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;policyName&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;awseb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zqag8vpi2p&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AWSEBAutoScalingScaleUpPolicy&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AQUPYQ179IYF&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Scaling&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;autoscaling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;us&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;east&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;538676797434&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;scalingPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="n"&gt;cd3027&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;bca&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="n"&gt;a8&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="n"&gt;feb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fe342e5b786a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;autoScalingGroupName&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;awseb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zqag8vpi2p&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AWSEBAutoScalingGroup&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;LS20VBXQLOU&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;policyName&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;awseb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zqag8vpi2p&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AWSEBAutoScalingScaleDownPolicy&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;CMAG2FZD4Y0B&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CloudWatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;alarm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;awseb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zqag8vpi2p&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AWSEBCloudwatchAlarmLow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;HWA2PL3FJCDQ&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CloudWatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;alarm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;awseb&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zqag8vpi2p&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;AWSEBCloudwatchAlarmHigh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="n"&gt;EGWT2CXU238&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;development&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xyxifvqn9z&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;us&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;east&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="n"&gt;elasticbeanstalk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Successfully&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;launched&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;development&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Creates an "environment" (because we like to separate Dev, Test, and Production ... which means auto creating an S3 bucket, Elastic IP, and security group, etc.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can examine the application in the WebUI:&lt;/p&gt;
&lt;p&gt;By using AWS Web Console -&amp;gt; Services -&amp;gt; Elastic Beanstalk -&amp;gt; "development" , to see configuration/status&lt;/p&gt;
&lt;p&gt;https://console.aws.amazon.com/elasticbeanstalk/home?region=us-east-1#/launchEnvironment?applicationName=ebjohnexample&amp;amp;environmentId=e-zqag8vpi2p&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl development.xyxifvqn9z.us-east-1.elasticbeanstalk.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;eb status --verbose&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Verify the Status and Health&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Environment details for: development
  Application name: ebjohnexample
  Region: us-east-1
  Deployed Version: app-191020_203345
  Environment ID: e-zqag8vpi2p
  Platform: arn:aws:elasticbeanstalk:us-east-1::platform/Python 3.6 running on 64bit Amazon Linux/2.9.3
  Tier: WebServer-Standard-1.0
  CNAME: development.xyxifvqn9z.us-east-1.elasticbeanstalk.com
  Updated: 2019-10-21 03:37:10.841000+00:00
  Status: Ready
  Health: Green
  Running instances: 1
      i-0365b4a823823ef5c: InService
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Since this was written AWS have created CodeCommit and Deploy for source control and deployment pipelines so even simpler (tighter) integration with Amazon&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="alternatively-using-the-aws-console-ui"&gt;Alternatively using the AWS Console UI&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/GettingStarted.CreateApp.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/GettingStarted.CreateApp.html&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="cleanup-and-pricing"&gt;Cleanup and Pricing&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;eb terminate development&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;terminate all instances, load balancers, etc. and delete the app from S3 entirely (since s3 costs money too)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Probably having a better name than just development might make it clearer if there are multiple applications in EB&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Clearly there is very little operational management required: developers have AWS infrastructure at their fingertips.&lt;/p&gt;
&lt;p&gt;The EC2 instances &lt;em&gt;charged per hour cough-cough&lt;/em&gt; costs depend on what sizing you use, t2.micro for a low traffic demo project is near nothing...&lt;/p&gt;
&lt;p&gt;But the load balancer at around $.02 an hour comes out to about 50 cents a day or &lt;em&gt;at least ~$15 a month&lt;/em&gt; even if your application is doing nothing.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://aws.amazon.com/elasticloadbalancing/pricing/"&gt;https://aws.amazon.com/elasticloadbalancing/pricing/&lt;/a&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h3 id="logs"&gt;Logs&lt;/h3&gt;
&lt;p&gt;AWS Web Console -&amp;gt; Deployment -&amp;gt; Elastic Beanstalk -&amp;gt; App Name -&amp;gt; Logs&lt;/p&gt;
&lt;p&gt;Snapshot Logs -&amp;gt; View Log File&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;notice that ElasticBeanstalk leverages CloudFormation under the hood
/opt/elasticbeanstalk/hooks/preinit/03wsgiuser.s
also uses virtualenv and pip&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2 id="credentials-via-environment-variables"&gt;Credentials via Environment Variables&lt;/h2&gt;
&lt;p&gt;Environment Variables help keep configuration out of your code (e.g. access keys/passwords)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mkdir .ebextensions&lt;/code&gt;
&lt;code&gt;vi .ebextensions/01.config&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;configs are read and run sequentially&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;BUT oddly enough .config files in ElasticBeanstalk actually need to be committed to the repo (security fail!)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/14206760/how-to-set-an-environment-variable-in-amazon-elastic-beanstalk-python"&gt;https://stackoverflow.com/questions/14206760/how-to-set-an-environment-variable-in-amazon-elastic-beanstalk-python&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;override these placeholders with actual secret values,&lt;/p&gt;
&lt;p&gt;AWS Web Console -&amp;gt; Deployment -&amp;gt; Elastic Beanstalk -&amp;gt; App Name -&amp;gt; Configuration -&amp;gt; Software Configuration (Gear Symbol)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; option_settings:
  &lt;span class="k"&gt;-&lt;/span&gt; option_name: PARAM1
    value: placeholder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="more-info"&gt;More info&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.managing.ec2.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.managing.ec2.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/configuring-https.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/configuring-https.html&lt;/a&gt; to have your &lt;em&gt;own domain and SSL&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.managing.elb.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.managing.elb.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;container_commands&lt;/strong&gt;
&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_Python_django.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_Python_django.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_Python_custom_container.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_Python_custom_container.html&lt;/a&gt;
&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Docker&lt;/strong&gt;
&lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_docker_eb.html"&gt;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_docker_eb.html&lt;/a&gt;&lt;/p&gt;</content><category term="build-CI-CD-devops"/><category term="aws"/><category term="eb"/><category term="elastic beanstalk"/><category term="python"/><category term="flask"/></entry><entry><title>Publish a pelican blog using a Bitbucket POST Webhook</title><link href="https://blog.john-pfeiffer.com/publish-a-pelican-blog-using-a-bitbucket-post-webhook/" rel="alternate"/><published>2014-08-08T06:00:00-07:00</published><updated>2014-08-08T06:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-08-08:/publish-a-pelican-blog-using-a-bitbucket-post-webhook/</id><summary type="html">
&lt;p&gt;Webhooks are an incredibly useful way to tie together disparate network parts, WHEN something happens in one place, it sends a POST HTTP request to another place.&lt;/p&gt;
&lt;h3 id="create-the-bitbucket-webhook-and-setup-a-server-to-receive-the-webhook"&gt;Create the Bitbucket Webhook and Setup a Server to Receive the Webhook&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Log in to the Bitbucket WebUI&lt;/li&gt;
&lt;li&gt;Choose the repository&lt;/li&gt;
&lt;li&gt;Choose to …&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">
&lt;p&gt;Webhooks are an incredibly useful way to tie together disparate network parts, WHEN something happens in one place, it sends a POST HTTP request to another place.&lt;/p&gt;
&lt;h3 id="create-the-bitbucket-webhook-and-setup-a-server-to-receive-the-webhook"&gt;Create the Bitbucket Webhook and Setup a Server to Receive the Webhook&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Log in to the Bitbucket WebUI&lt;/li&gt;
&lt;li&gt;Choose the repository&lt;/li&gt;
&lt;li&gt;Choose to administer the repository (gear symbol) -&amp;gt; Hooks (left menu) , or simply &lt;a href="https://bitbucket.org/username/reponame/admin/hooks"&gt;https://bitbucket.org/username/reponame/admin/hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Select Hook Type (dropdown) , POST , Add Hook (Button)&lt;/li&gt;
&lt;li&gt;Enter your target URL, SAVE&lt;/li&gt;
&lt;li&gt;Setup a webserver (easiest might be Bamboo or Jenkins) somewhere&lt;/li&gt;
&lt;li&gt;Ensure there is a URL that accepts POST requests&lt;/li&gt;
&lt;li&gt;Ensure that when the POST is received it runs the pelican content generation commands to make the new output&lt;/li&gt;
&lt;li&gt;Ensure new output is visible&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;You may notice any existing POST webhooks, i.e. a HipChat notification add-on, listed: https://hipchat-bitbucket.herokuapp.com/commit?client_id=f955ddb5&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="flask-and-bash-source-code-to-publish-a-pelican-static-web-site"&gt;Flask and Bash source code to publish a pelican static web site&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;This custom solution requires running that flask app manually, i.e. python mypublish.py
It also requires having two repositories, one for the pelican source content, 
the other repo (i.e. a bitbucket static web site) will only contain the output (.html files)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="vi-mypublishpy"&gt;vi mypublish.py&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PIPE&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/someuniquekeyhere'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mypublish&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"./mypublish.sh"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'__main__'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'0.0.0.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8443&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;use_reloader&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="vi-mypublishsh"&gt;vi mypublish.sh&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;pull
&lt;span class="nv"&gt;GITMESSAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;log&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"../outputreponame.bitbucket.io"&lt;/span&gt;
./clean-output.sh&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"../sourcereponame.bitbucket.io"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# removes all of the old content&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITMESSAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
pelican&lt;span class="w"&gt; &lt;/span&gt;content
cp&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;./output/*&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT&lt;/span&gt;

rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;./output
rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;./cache
rm&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;*.pyc

&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;--all&lt;span class="w"&gt; &lt;/span&gt;./content
git&lt;span class="w"&gt; &lt;/span&gt;commit&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"source &lt;/span&gt;&lt;span class="nv"&gt;$GITMESSAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="vi-clean-outputsh"&gt;vi clean-output.sh&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;./output
rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;./cache
rm&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;*.pyc

&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ITEM&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$SOURCE&lt;/span&gt;/*
&lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ITEM&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ITEM&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ITEM&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="more-info"&gt;More info&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://read-the-docs.readthedocs.org/en/latest/webhooks.html"&gt;https://read-the-docs.readthedocs.org/en/latest/webhooks.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://confluence.atlassian.com/bitbucket/manage-webhooks-735643732.html"&gt;https://confluence.atlassian.com/bitbucket/manage-webhooks-735643732.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="programming"/><category term="pelican"/><category term="webhook"/><category term="bitbucket"/><category term="flash"/></entry><entry><title>Using Vagrant to deploy instances on AWS</title><link href="https://blog.john-pfeiffer.com/using-vagrant-to-deploy-instances-on-aws/" rel="alternate"/><published>2014-07-16T21:12:00-07:00</published><updated>2014-07-16T21:12:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-07-16:/using-vagrant-to-deploy-instances-on-aws/</id><summary type="html">
&lt;p&gt;Vagrant is an infrastructure tool that simplifies deployment, such as virtual machines or in this case Amazon EC2 instances.&lt;/p&gt;
&lt;h3 id="install-vagrant-and-the-vagrant-aws-plugin"&gt;Install Vagrant and the Vagrant AWS plugin&lt;/h3&gt;
&lt;p&gt;Download and install vagrant: &lt;strong&gt;&lt;a href="https://www.vagrantup.com/downloads"&gt;https://www.vagrantup.com/downloads&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;wget https://releases.hashicorp.com/vagrant/1.7.4/vagrant_1.7.4_x86_64.deb
dpkg -i …&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">
&lt;p&gt;Vagrant is an infrastructure tool that simplifies deployment, such as virtual machines or in this case Amazon EC2 instances.&lt;/p&gt;
&lt;h3 id="install-vagrant-and-the-vagrant-aws-plugin"&gt;Install Vagrant and the Vagrant AWS plugin&lt;/h3&gt;
&lt;p&gt;Download and install vagrant: &lt;strong&gt;&lt;a href="https://www.vagrantup.com/downloads"&gt;https://www.vagrantup.com/downloads&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;wget https://releases.hashicorp.com/vagrant/1.7.4/vagrant_1.7.4_x86_64.deb
dpkg -i vagrant_1.7.4_x86_64.deb
vagrant --version
vagrant plugin install vagrant-aws
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="quickstart-vagrant-and-virtualbox-with-ubuntu-trusty-1404"&gt;Quickstart Vagrant and VirtualBox with Ubuntu Trusty 14.04&lt;/h3&gt;
&lt;p&gt;The simple local VirtualBox method was:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;virtualbox&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nv"&gt;help&lt;/span&gt;
&lt;span class="nv"&gt;vagrant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;init&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;trusty64&lt;/span&gt;&lt;span class="c1"&gt;; vagrant up --provider virtualbox&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'ubuntu/trusty64'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;could&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;found&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Attempting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;install&lt;/span&gt;...
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Provider&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;virtualbox&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Version&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Loading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;metadata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'ubuntu/trusty64'&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;URL&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;https&lt;/span&gt;:&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nv"&gt;atlas&lt;/span&gt;.&lt;span class="nv"&gt;hashicorp&lt;/span&gt;.&lt;span class="nv"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;trusty64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;this will do all the extra work for you of finding and downloading the "box" and starting it in VirtualBox&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="vagrant-and-aws-ec2-with-ubuntu-precise-1204"&gt;Vagrant and AWS EC2 with Ubuntu Precise 12.04&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;vagrant box add dummy https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://atlas.hashicorp.com/boxes/search"&gt;https://atlas.hashicorp.com/boxes/search&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;vagrant box list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vi Vagrantfile&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;Vagrant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"dummy"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_key_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"YOURACCESSKEY"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secret_access_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"YOURSECRETKEY"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keypair_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"YOURKEYPAIRNAME"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ami&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"ami-7747d01e"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_ready_timeout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"m4.large"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;"Name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"MyCloudInstance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"dummy"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;box_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"ubuntu"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_key_path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"./YOURKEYPAIRNAME.pem"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;
&lt;span class="nx"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;ubuntu/images/ubuntu-precise-12.04-amd64-server-20130204 - ami-7747d01e , no ebs storage - just instance storage , &lt;a href="https://cloud-images.ubuntu.com/releases/"&gt;https://cloud-images.ubuntu.com/releases/&lt;/a&gt; and &lt;a href="https://atlas.hashicorp.com/boxes/search"&gt;https://atlas.hashicorp.com/boxes/search&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;vagrant will by default upload all folders and files in your "project" folder where the Vagrantfile is located&lt;/p&gt;
&lt;p&gt;vagrant will start with the current working directory and look for a Vagrantfile, then go up one directory until it finds one: &lt;a href="https://docs.vagrantup.com/v2/vagrantfile/"&gt;https://docs.vagrantup.com/v2/vagrantfile/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;vagrant up --provider=aws --debug&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;--debug is interactive mode and requires pressing enter between every step&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;Bringing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;machine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HandleBoxUrl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;middleware&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;deprecated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HandleBox&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;instead&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Please&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;creator&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Warning&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AWS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;doesn&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;support&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Vagrant&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;configurations&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;They&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;silently&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ignored&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Launching&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;following&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;large&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AMI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ami&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7747&lt;/span&gt;&lt;span class="nx"&gt;d01e&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;us&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;east&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;YOURKEYPAIRHERE&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Device&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Mapping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Terminate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;On&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Shutdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Monitoring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;EBS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;optimized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Assigning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;VPC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Waiting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;become&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"ready"&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Waiting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SSH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;become&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Machine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;booted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Rsyncing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;folder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;myproject&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;vagrant&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;vagrant ssh&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the default-easiest-interface way of getting SSH access into the machine&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant ssh-config
vagrant ssh-config &amp;gt; vagrant-ssh
ssh -F vagrant-ssh default
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;alternative interactive ssh session: Use the HostName or AWS EC2 WebUI or &lt;a href="http://aws.amazon.com/cli"&gt;http://aws.amazon.com/cli&lt;/a&gt; to discover the remote machine IP address
using ssh with the vagrant-ssh file output seems the simplest
ssh -i YOURKEYPAIRHERE.pem ubuntu@1.2.3.4 ls -ahl /vagrant&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;exit&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vagrant ssh -c "ls -ahl"&lt;/code&gt; for a non interactive listing of the home directory&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;vagrant ssh -c "pidof ntpd | xargs sudo kill -9"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;vagrant up&lt;/code&gt; , &lt;code&gt;vagrant reload&lt;/code&gt; , and &lt;code&gt;vagrant provision&lt;/code&gt; will have the AWS provider use rsync to push data to /vagrant&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.vagrantup.com/v2/synced-folders/rsync.html"&gt;https://docs.vagrantup.com/v2/synced-folders/rsync.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vagrant stop&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;UnsupportedOperation =&amp;gt; The instance 'i-1295bf39' does not have an 'ebs' root device type and cannot be stopped. (Fog::Compute::AWS::Error)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;vagrant destroy&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt; default: Are you sure you want to destroy the 'default' VM? [y/N] Y
&amp;gt;
&amp;gt; ==&amp;gt; default: Terminating the instance...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;vagrant destroy -f&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;non interactively destroy the instance and avoid the misleading error message: Vagrant is attempting to interface with the UI in a way that requires a TTY&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="vagrant-provisioning"&gt;Vagrant provisioning&lt;/h3&gt;
&lt;p&gt;Allows for automated installation of software bundled into the &lt;code&gt;vagrant up&lt;/code&gt; command&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vagrant up --provider=aws --no-provision&lt;/code&gt; to prevent any provisioning&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;config.vm.provision "shell", inline: "echo Hello, World"

config.vm.provision "shell", path: "script.sh"

config.vm.provision "shell", path: "https://example.com/script.sh"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://docs.vagrantup.com/v2/provisioning"&gt;http://docs.vagrantup.com/v2/provisioning&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://docs.vagrantup.com/v2/provisioning/shell.html"&gt;http://docs.vagrantup.com/v2/provisioning/shell.html&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="advanced-vagrantfile-example"&gt;Advanced Vagrantfile example&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# -*- mode: ruby -*-&lt;/span&gt;
&lt;span class="c1"&gt;# vi: set ft=ruby :&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'UPDATEFQDN'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;updatedfqdn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'UPDATEFQDN'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="vg"&gt;$fqdnscript&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;FQDNSCRIPT&lt;/span&gt;
&lt;span class="sh"&gt;echo "I am updating fqdn to #{updatedfqdn}..."&lt;/span&gt;
&lt;span class="sh"&gt;cat /etc/hosts | grep "#{updatedfqdn}" || sudo sed 's/127.0.0.1/127.0.0.1 #{updatedfqdn}/' -i /etc/hosts&lt;/span&gt;
&lt;span class="sh"&gt;hostname | grep "#{updatedfqdn}" || sudo hostname #{updatedfqdn}&lt;/span&gt;
&lt;span class="dl"&gt;FQDNSCRIPT&lt;/span&gt;

&lt;span class="no"&gt;Vagrant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dummy"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;access_key_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOURACCESSKEY"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;secret_access_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOURSECRETKEY"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keypair_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOURKEYPAIRNAME"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ami&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ami-7747d01e"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_ready_timeout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"m4.large"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s2"&gt;"Name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MyCloudInstance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'bamboo_aws_use_iops'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_device_mapping&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'DeviceName'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/dev/sda1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Ebs.VolumeSize'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Ebs.VolumeType'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'io1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Ebs.Iops'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_device_mapping&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'DeviceName'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/dev/sda1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Ebs.VolumeSize'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Ebs.VolumeType'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'gp2'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dummy"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;private_key_path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./YOURKEYPAIRNAME.pem"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;synced_folder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/etc/sslcerts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/tmp/sourcecode"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rsync"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;rsync__exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".git/"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provision&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:shell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:inline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo `hostname -f` &amp;gt;&amp;gt; /home/ubuntu/currenthostname.txt"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provision&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:shell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:inline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="vg"&gt;$fqdnscript&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Bringing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'default'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aws'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;Running&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;provisioner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;
&lt;span class="ss"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ttty&lt;/span&gt;
&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;am&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;provisioning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;updating&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;synced_folder is to sync other folders in your filesystem besides the folder with the Vagrantfile&lt;/p&gt;
&lt;p&gt;config.vm.hostname does not appear to work on AWS EC2 so the workaround above (|| statements to prevent extra reconfiguration)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/mitchellh/vagrant-aws"&gt;https://github.com/mitchellh/vagrant-aws&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html"&gt;http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="vagrant-and-ec2-vpc-ami-that-does-not-haveallow-sudo"&gt;Vagrant and EC2 VPC (AMI that does not have/allow sudo)&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# -*- mode: ruby -*-&lt;/span&gt;
&lt;span class="c1"&gt;# vi: set ft=ruby :&lt;/span&gt;

&lt;span class="no"&gt;Vagrant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dummy"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;synced_folder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/vagrant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;access_key_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOURACCESSKEY"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;secret_access_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOURSECRETKEY"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keypair_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOURKEYPAIRNAME"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ami&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ami-7747d01e"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_ready_timeout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"m4.large"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s2"&gt;"Name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MyCloudInstance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;security_groups&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my_aws_security_group_id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subnet_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my_aws_subnet_id"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;associate_public_ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#cloud-boothook&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;#!/bin/bash&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;touch /opt/.license/.eula&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dummy"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;private_key_path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/ubuntu/my_aws.pem"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr/&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;vagrant up --provider=aws --no-provision --debug&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`vagrant`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;invoked&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"up"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--provider=aws"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--no-provision"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--debug"&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;Bringing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'default'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aws'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Launching&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;following&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="n"&gt;tings&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;-- Type: m4.large&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;-- User Data: #cloud-boothook&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;-- Assigning a public IP address in a VPC: true&lt;/span&gt;
&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Waiting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;become&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ready"&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Waiting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SSH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;become&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;SSH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;level&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SSH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ready&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Re&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SSH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Execute&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;obtained&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;connection&lt;/span&gt;
&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TERM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vt100&lt;/span&gt;

&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;JOHN&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cloud&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;boothook&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;here&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;

&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logout&lt;/span&gt;
&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run_instance&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SSH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ready&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;48.444087982177734&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Machine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;booted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ready&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rsync&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;
&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rsync&lt;/span&gt;

&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Machine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;provisioning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;because&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`--no-provision`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;specified&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;After all of that it is safe to either have &lt;code&gt;vagrant provision&lt;/code&gt; or &lt;code&gt;vagrant ssh -c "ls -ahl"&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Using a pseudo tty&lt;/strong&gt; is a required workaround if using an AMI that does not support tty / sudo (i.e. Amazon's default Linux AMI)&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.vagrantup.com/v2/vagrantfile/ssh_settings.html"&gt;https://docs.vagrantup.com/v2/vagrantfile/ssh_settings.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/mitchellh/vagrant/issues/1482"&gt;https://github.com/mitchellh/vagrant/issues/1482&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disabling the /vagrant synced project folder&lt;/strong&gt; is nice if you don't automatically want the Vagrantfile and everything in there rsynced to your EC2 instance (and avoids the ugly &lt;code&gt;mkdir -p /vagrant&lt;/code&gt; which requires sudo)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS User Data&lt;/strong&gt; can be pushed in via Vagrant which allows for custom scripts / commands / package installation during the EC2 instance boot&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://help.ubuntu.com/community/CloudInit"&gt;https://help.ubuntu.com/community/CloudInit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloudinit.readthedocs.org/en/latest/topics/format.html#cloud-boothook"&gt;https://cloudinit.readthedocs.org/en/latest/topics/format.html#cloud-boothook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/17413598/vagrant-rsync-error-before-provisioning"&gt;http://stackoverflow.com/questions/17413598/vagrant-rsync-error-before-provisioning&lt;/a&gt;&lt;blockquote&gt;
&lt;p&gt;One of the use cases for an aws.user_data #cloud-boothook script has been to add to /etc/sudoers.d/ (thus avoiding later sudo issues with rsync)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="virtualbox"&gt;VirtualBox&lt;/h3&gt;
&lt;p&gt;A really easy A way to start an Ubuntu 14.04 box locally with VirtualBox, the shell provisioner is less elegant than chef/puppet/ansible but gets the job done (installs Docker and Docker Compose)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.virtualbox.org/wiki/Linux_Downloads"&gt;https://www.virtualbox.org/wiki/Linux_Downloads&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="vagrantfile"&gt;Vagrantfile&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;VERSIONS = {
  'trusty' =&amp;gt; {
    'box' =&amp;gt; "canonical-ubuntu-14.04",
    'box_url' =&amp;gt; "https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box",
    'ami' =&amp;gt; "ami-018c9568",
  },
}


Vagrant.configure("2") do |config|
    config.ssh.forward_agent = true

    version = VERSIONS[("trusty")]

    config.vm.provider "virtualbox" do |v, override|
      v.customize ["modifyvm", :id, "--memory", ENV['VM_MEMORY'] || 4096]
      v.customize ["modifyvm", :id, "--cpus", ENV['VM_CPUS'] || 2]

      override.vm.network :private_network, ip: "192.168.33.10"
      override.vm.box = version['box']
      override.vm.box_url = version['box_url']
    end

    config.vm.provision :shell, :inline =&amp;gt; "sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9"
    config.vm.provision :shell, :inline =&amp;gt; "sudo sh -c 'echo deb https://get.docker.io/ubuntu docker main &amp;gt; /etc/apt/sources.list.d/docker.list'"
    config.vm.provision :shell, :inline =&amp;gt; "sudo apt-get update"
    config.vm.provision :shell, :inline =&amp;gt; "sudo apt-get install -y lxc-docker python-pip"
    config.vm.provision :shell, :inline =&amp;gt; "sudo pip install --upgrade pip"
    config.vm.provision :shell, :inline =&amp;gt; "sudo pip install --upgrade docker-compose"

end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="more-info"&gt;more info&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ps aux | grep vagrant&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;nothing to see here but there is still state for machines started...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant global-status
vagrant global-status --prune
rm -rf .vagrant
rm -rf /home/ubuntu/.vagrant.d
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;If you use vagrant 1.7 don not be surprised if you see errors related to SSL, 1.6.3 FTW&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/mitchellh/vagrant-aws"&gt;https://github.com/mitchellh/vagrant-aws&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="troubleshooting"&gt;Troubleshooting&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;vagrant up --provider=aws --debug&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ERROR: The provider 'aws' could not be found, but was requested to back the machine 'default'. Please use a provider that exists.&lt;/p&gt;
&lt;p&gt;RESOLUTION: try re-installing the vagrant-aws plugin again and immediately running the vagrant up command afterwards&lt;/p&gt;
&lt;p&gt;ERROR: Timeout when waiting for SSH , SSH not up: ... The private key to connect to the machine via SSH must be owned&lt;/p&gt;
&lt;p&gt;RESOLUTION: chown root:root  and chmod 400&lt;/p&gt;
&lt;p&gt;ERROR: INFO ssh: SSH not up: #&amp;lt;Vagrant::Errors::SSHAuthenticationFailed: SSH authentication failed! This is typically caused by the public/private keypair for the SSH user not being properly set on the guest VM&lt;/p&gt;
&lt;p&gt;RESOLUTION: ensure the correct user, i.e. ec2-user or ubuntu etc. is used in the override.ssh.username to match what the AMI expects&lt;/p&gt;
&lt;p&gt;ERROR: sudo: no tty present and no askpass program specified&lt;/p&gt;
&lt;p&gt;RESOLUTION: your VM (or more likely, AMI) does not have tty or allow sudo so try using the config.ssh.pty = true (and make sure no provisioning commands require sudo)&lt;/p&gt;
&lt;/blockquote&gt;</content><category term="virtualization"/></entry><entry><title>Docker Intro install run and port forward</title><link href="https://blog.john-pfeiffer.com/docker-intro-install-run-and-port-forward/" rel="alternate"/><published>2014-07-10T17:00:00-07:00</published><updated>2014-07-10T17:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-07-10:/docker-intro-install-run-and-port-forward/</id><summary type="html">&lt;p&gt;Docker is a union file system based layer system (previously leveraging linux lxc containers) for ultra lightweight virtualization/compartmentalization.&lt;/p&gt;
&lt;p&gt;Much like AWS cloud servers (api based dynamic deployment that should be tolerant of node failure) and automated deployment/configuration infrastructure (chef or puppet such that cloud servers are created idempotent …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Docker is a union file system based layer system (previously leveraging linux lxc containers) for ultra lightweight virtualization/compartmentalization.&lt;/p&gt;
&lt;p&gt;Much like AWS cloud servers (api based dynamic deployment that should be tolerant of node failure) and automated deployment/configuration infrastructure (chef or puppet such that cloud servers are created idempotent, remotely and automatically managed at scale), Docker requires a change of mindset.&lt;/p&gt;
&lt;p&gt;Docker encourages design of modular, deterministic and defined, single purpose components that are easy to compose into larger services.&lt;/p&gt;
&lt;p&gt;As any tool, using it for managing complexity and packaging can be very helpful but it does expose other potential issues (composability, orchestration, security).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Images&lt;/strong&gt; are the initial templates, each image has a unique ID &lt;a href="https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/#content-addressable-storage"&gt;https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/#content-addressable-storage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Containers&lt;/strong&gt; are the running virtual machines, each container has a unique ID  &lt;a href="https://en.wikipedia.org/wiki/Operating-system-level_virtualization"&gt;https://en.wikipedia.org/wiki/Operating-system-level_virtualization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;From now on it is assumed you use &lt;/em&gt;&lt;em&gt;sudo&lt;/em&gt;&lt;em&gt; before any docker command!&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id="install-docker"&gt;Install Docker&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://docs.docker.com/engine/installation/linux/ubuntulinux/"&gt;https://docs.docker.com/engine/installation/linux/ubuntulinux/&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo sh -c "echo 'deb https://apt.dockerproject.org/repo ubuntu-trusty main' &amp;gt; /etc/apt/sources.list.d/docker.list"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo apt-get update&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apt-get install linux-image-extra-$(uname -r)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apt-get install docker-engine&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service docker status&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker info&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Make sure it lists /var/lib/docker/aufs&lt;/p&gt;
&lt;p&gt;OPTIONAL STEP IF YOU HAD AN OLD DOCKER INSTALLATION
&lt;code&gt;apt-get purge lxc-docker*&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="installing-on-mac-or-windows"&gt;Installing on Mac or Windows&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.docker.com/docker-toolbox"&gt;https://www.docker.com/docker-toolbox&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="custom-docker0-ip-range"&gt;Custom docker0 ip range&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Fixing the Docker Bridge docker0 taking a huge 172.17.0.1/16 address space...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Private IP address space is not normally a thing to worry about, unless someone does something silly and grabs 65,534 addresses (stare) (docker)&lt;/p&gt;
&lt;p&gt;Because the default docker0 bridge seems to cater to organizations that want to run thousands of containers simultaneously local developers need to do the following fix:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;apt-get install bridge-utils&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service docker stop&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ip link set docker0 down&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;brctl delbr docker0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker daemon --bip=192.168.239.1/24&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ifconfig&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Docker will be running interactively so you can see all the fun log messages&lt;/p&gt;
&lt;p&gt;You should see docker0 "inet addr:192.168.239.1  Bcast:0.0.0.0  Mask:255.255.255.0"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Assuming you do not have some other purpose for the 192.168.239 range in which case you can change it to something else&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To permanently have this custom ip range configuration for docker (assuming you have done the steps above):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Control + C to quit the previously interactive docker daemon&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vi /etc/default/docker&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DOCKER_OPTS="--bip=192.168.239.1/24"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service docker start&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ifconfig&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;You should see docker0 "inet addr:192.168.239.1  Bcast:0.0.0.0  Mask:255.255.255.0"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;More docs to further troubleshoot the docker0 bridge...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/userguide/networking/default_network/custom-docker0/"&gt;https://docs.docker.com/engine/userguide/networking/default_network/custom-docker0/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/admin/systemd/"&gt;https://docs.docker.com/engine/admin/systemd/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id="quick-start-summary"&gt;Quick Start Summary&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run --rm busybox /bin/sh -c "echo 'hi'"
hi

docker run -it --rm -e MYVAR=123 busybox env
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;"run" will pull the image from Docker Hub by default, e injects an environment variable, overrides the Docker Image CMD with "env"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run -it --rm --entrypoint=/bin/sh python:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;run the docker container (from the latest public python image) and start the shell prompt instead of the python interpreter&lt;/li&gt;
&lt;li&gt;the entrypoint parameter overrides the Docker Image (in case they do not provide a helpfully overridable CMD)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/reference/builder/#entrypoint"&gt;https://docs.docker.com/engine/reference/builder/#entrypoint&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id="download-a-docker-image"&gt;Download a docker image&lt;/h2&gt;
&lt;p&gt;Official Images are he easiest to experiment with: &lt;a href="https://hub.docker.com/explore/"&gt;https://hub.docker.com/explore/&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo docker pull ubuntu:trusty
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;(&lt;strong&gt;grabs the latest, i.e. 14.04.1&lt;/strong&gt;) or use a different tag to download a more specific version &lt;code&gt;sudo docker pull ubuntu:12.04.3&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;CRITICAL WARNING!&lt;/strong&gt; use the colon and a specific version! downloading all of the ubuntu images by accident sucks =(&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo docker pull redis:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;choose the latest for just messing around but...
ALWAYS use a specific version to avoid having your dependencies change unexpectedly&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finding what versions of images (tags) you can pull requires using either the UI or the API:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/r/library/redis/tags/"&gt;https://hub.docker.com/r/library/redis/tags/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker pull redis:3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker pull redis:2.8&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker pull redis:2.6&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Many of the tags are synonyms/symlinks, so latest is the same as 3 is the same as 3.0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="remove-a-docker-image"&gt;Remove a docker image&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker rmi redis

docker rmi -f $(docker images --all --quiet | grep -v 5506de2b643b)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;remove ALL images except one by taking the output (quiet means only image ids), excluding a specific one, and then force removing the images (by id)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker images --quiet --filter "dangling=true" | xargs docker rmi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;remove all images that do not have a tag and are not a parent of a tagged image&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker rmi -f $(docker images --all --quiet)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;remove ALL images&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;du&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aufs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Summarize the amount of disk space taken by images and layers, e.g.: 72K    /var/lib/docker/aufs/&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sometimes a docker image is &lt;strong&gt;still connected to a container&lt;/strong&gt; (already exited or forgotten)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker ps -a
docker rm name_or_id

docker rm a1b2
docker rmi image_id
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;it will "smart match" the first characters of the container ID&lt;/strong&gt; the same as git short sha&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="docker-info"&gt;Docker info&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;get a helpful list of all the commands&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;

&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;

&lt;span class="p"&gt;:::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
&lt;span class="n"&gt;Containers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;Images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;
&lt;span class="n"&gt;Storage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Driver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aufs&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aufs&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Dirs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;
&lt;span class="n"&gt;Execution&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Driver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;native&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;
&lt;span class="n"&gt;Kernel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.13&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;generic&lt;/span&gt;
&lt;span class="n"&gt;Operating&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ubuntu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;14.04&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LTS&lt;/span&gt;
&lt;span class="n"&gt;WARNING&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;support&lt;/span&gt;

&lt;span class="n"&gt;du&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aufs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;469M    /var/lib/docker/aufs/&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker ps --all
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;no containers are running yet&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker ps --help
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;docker images --tree&lt;/strong&gt; is a deprecated command to view the hashes and sizes of all of the parent images&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id="controlling-containers"&gt;Controlling Containers&lt;/h2&gt;
&lt;h3 id="starting-a-container-from-an-image"&gt;Starting a container from an image&lt;/h3&gt;
&lt;h4 id="create-a-container"&gt;Create a container&lt;/h4&gt;
&lt;p&gt;First you must create a container from an image:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;get a helpful list of how to run a container&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nv"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ubuntu&lt;/span&gt;:&lt;span class="mi"&gt;14&lt;/span&gt;.&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;bash&lt;/span&gt;

:::&lt;span class="nv"&gt;text&lt;/span&gt;
&lt;span class="nv"&gt;creates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;container&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nv"&gt;rm&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;automatically&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;remove&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;container&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;exits&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stdin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;open&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;even&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;attached&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;t&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;allocate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tty&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;attach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stdin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stdout&lt;/span&gt;
&lt;span class="nv"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ubuntu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;.&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;minimal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;image&lt;/span&gt;
&lt;span class="nv"&gt;Docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;automatically&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;gives&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;container&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;
&lt;span class="nv"&gt;Runs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;interactive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;shell&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This will continue to exist in a stopped state once exited (see "docker ps -a")&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="nv"&gt;@f5878ed6016e&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;
&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="nv"&gt;@f5878ed6016e&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="nv"&gt;@f5878ed6016e&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Control-p then Control-q to detach the tty without exiting the shell&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker ps

docker run --detach --name myapp -p 127.0.0.1:5000:5000 training/webapp python app.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;detached with port 5000 available only to the host and executing the command python with parameter app.py&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;myapp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;ahl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;runs the ls command inside the container named "myapp"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker run --rm -i -t --link myhipchat_mariadb_1 mariadb:5 /bin/bash -c "exec mysql --version"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker run --rm -i -t --link myhipchat_mariadb_1:mysql mariadb:5 /bin/bash -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p'&lt;/code&gt;&lt;blockquote&gt;
&lt;p&gt;an Image can contain both the server and client code so run a "client container" to connect to a running server Container &lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="start-resume-a-container"&gt;Start (resume) a container&lt;/h4&gt;
&lt;p&gt;After a container has already been created (which starts it so ironically this is actually a "restart")&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker start --interactive --attach container_id_or_name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="attaching-to-a-running-container"&gt;Attaching to a running container&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker ps -a 
CONTAINER ID ... STATUS        NAMES
9e0ebf4421dd ... Up 6 seconds  myexample

docker attach 9e0ebf4421dd
docker attach myexample
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;since the above will expect the container to have /bin/bash it will reuse the instance of shell&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="nv"&gt;e0ebf4421dd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;bash&lt;/span&gt;
#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;aux&lt;/span&gt;
#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;instead a new /bin/bash is executed inside creating a second shell - use the exit command to not leave it around&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="running-a-container-with-admin-privileges"&gt;Running a container with admin privileges&lt;/h4&gt;
&lt;p&gt;If your container requires elevated privileges from the host then you need to provide the extra parameter:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker run --rm -it --cap-add SYS_NET_ADMIN alpine&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Alternatively give it all extended privileges and permissions:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker run --rm -it --privileged alpine&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities"&gt;https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="stopping-a-container"&gt;Stopping a container&lt;/h3&gt;
&lt;p&gt;Part of the efficiency in docker is that containers can &lt;strong&gt;run in the background automatically&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run --detach --name myredis redis
docker ps
docker stop myredis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Another efficiency is that a docker container will only &lt;strong&gt;run as long as it takes to execute&lt;/strong&gt; a command (and any &lt;strong&gt;changes are not forgotten&lt;/strong&gt;)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run ubuntu:trusty uname -a
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;this runs the container only as long as it takes to execute the command&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker attach f5878ed6016e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Control + C&lt;/code&gt; &lt;em&gt;(now we have exited the container and it will clean itself up)&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker ps -a
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;spun up another container but only long enough to run the command&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;CONTAINER ID IMAGE        COMMAND      CREATED      STATUS    PORTS         NAMES
e4b436320442 ubuntu:14.04 -uname -a  3 minutes ago              elegant_engelbart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="copying-a-file-out-of-a-container"&gt;Copying a file out of a container&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker cp &amp;lt;containerId&amp;gt;:/file/path/within/container /host/path/target
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://docs.docker.com/engine/reference/commandline/cp/"&gt;https://docs.docker.com/engine/reference/commandline/cp/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="deleting-aka-removing-a-container"&gt;Deleting aka removing a container&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker rm e4b436320442
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Alternatively: &lt;code&gt;docker rm --force elegant_engelbart&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker rm -f $(docker ps -a -q)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Deletes forcibly all containers (be careful!)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2 id="dockerfile-to-automate-building-an-image"&gt;Dockerfile to automate building an image&lt;/h2&gt;
&lt;p&gt;Dockerfiles allow automating the creation docker images.&lt;/p&gt;
&lt;p&gt;One advantage to Dockerfile is that each command creates a separate layer so if a specific layer fails all of the previous intermediate images can be re-used.&lt;/p&gt;
&lt;p&gt;Also the version style of imagename:tag allows for chaining of upgrades of child images&lt;/p&gt;
&lt;p&gt;Containers as fast, reliable, and deterministic prod/qa/dev environments can also be extended to be just an improved experimentation sandbox (for those used to SSH and using Linux as a common base OS).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir -p dockerfiles/trustyssh
vi dockerfiles/trustyssh/Dockerfile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;one Dockerfile per directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ubuntu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;trusty&lt;/span&gt;
&lt;span class="n"&gt;MAINTAINER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;John&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Pfeiffer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://bitbucket.org/johnpfeiffer"&lt;/span&gt;

&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;openssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;byobu&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sshd&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'root:root'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chpasswd&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'ubuntu:ubuntu'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chpasswd&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/^PermitRootLogin\s+.*/PermitRootLogin yes/'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sshd_config&lt;/span&gt;
&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/UsePAM yes/#UsePAM yes/g'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sshd_config&lt;/span&gt;
&lt;span class="n"&gt;EXPOSE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;
&lt;span class="n"&gt;CMD&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/usr/sbin/sshd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-D"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;newimagename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;dockerfiles&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;trusty&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Each RUN command creates an intermediate container, so make sure you use the -rm option&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Sending build context to Docker daemon  2.56 kB
Sending build context to Docker daemon
Step 0 : FROM ubuntu:trusty
 ---&amp;gt; 5506de2b643b
...
Removing intermediate container 135b686d82a6
Step 2 : RUN apt-get update -y
 ---&amp;gt; Running in 7fe06a9ef41b
Ign http://archive.ubuntu.com trusty InRelease
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since each run command creates a new layer it is best practice to consolidate commands for a single logical action when possible:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"#!/bin/bash"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"cd /opt/mydata/pelican-project"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"pelican content -r &amp;amp;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"cd /opt/mydata/pelican-project/output"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"python -m SimpleHTTPServer"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;chmod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;


&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;utopic&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;pelican&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;view the history of hashes (which can be run by themselves) and timestamps and sizes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;IMAGE               CREATED             CREATED BY                                      SIZE
39ac388d3c0d        37 seconds ago      /bin/sh -c #(nop) CMD [/bin/bash /root/build-   0 B
218edf407f18        37 seconds ago      /bin/sh -c echo "#!/bin/bash" &amp;gt; /root/build-a   129 B
d9d774d344bd        2 weeks ago         /bin/sh -c #(nop) EXPOSE 8000/tcp               0 B
ae1733e0e1b9        2 weeks ago         /bin/sh -c pip install pelican Markdown beaut   20.64 MB
24561ed8052f        2 weeks ago         /bin/sh -c curl https://bootstrap.pypa.io/get   9.826 MB
1878a9a052eb        2 weeks ago         /bin/sh -c apt-get update &amp;amp;&amp;amp; apt-get install    60.85 MB
5e5e0e9171da        2 weeks ago         /bin/sh -c #(nop) MAINTAINER John Pfeiffer "h   0 B
78949b1e1cfd        7 weeks ago         /bin/sh -c #(nop) CMD [/bin/bash]               0 B
21abcc4ef877        7 weeks ago         /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/   1.895 kB
f552c527d701        7 weeks ago         /bin/sh -c echo '#!/bin/sh' &amp;gt; /usr/sbin/polic   215 kB
c4c77a6165f9        7 weeks ago         /bin/sh -c #(nop) ADD file:24ed1895f2e500dcec   194.2 MB
511136ea3c5a        22 months ago                                                       0 B
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;experimental:
&lt;code&gt;docker save 49b5a7a88d5 | sudo docker-squash -t ubuntu-utopic-pelican:squash | docker load&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2 id="add-a-port-to-a-container"&gt;Add a port to a container&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;docker run --detach --publish 127.0.0.1:2222:22 --name johnssh trustyssh&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;run a detached container based on the trustyssh image that binds the private container port 22 to the host port 2222 (in this case the trustyssh image from above is running sshd on port 22)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;docker port johnssh 22&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;127.0.0.1:2222&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;netstat -antp | grep 2222&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;tcp        0      0 127.0.0.1:2222          0.0.0.0:*               LISTEN      24393/docker-proxy&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;ssh -o StrictHostKeychecking=no -p 2222 root@127.0.0.1&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;root@19ad0614b237:~#&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;command&lt;/th&gt;
&lt;th style="text-align: center;"&gt;output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;docker run --detach --publish 0.0.0.0:6379:6379 --name redis redis&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;feeb79581810a8c182202c73d4e1c6b905960bcfc860e04285f1ae03c6a47f18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;docker port redis&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;6379/tcp -&amp;gt; 0.0.0.0:6379&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;docker port redis 6379&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;0.0.0.0:6379&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;redis-cli -h docker.example.com ping&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;PONG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;docker run --detach --publish-all --name redis redis&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;ff2f6d2e04d565f11d71664bf6cf23638656d9b633e4d3c94444c81b18b807bb&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;docker port redis&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;6379/tcp -&amp;gt; 0.0.0.0:49153&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;docker port redis 6379&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;0.0.0.0:49153&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;docker run --detach -P --name johnssh trustyssh&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;publish all EXPOSE'd ports to random ports on the host&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="logs-from-the-containers"&gt;Logs from the containers&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;docker logs container_name&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;view the latest logs of a specific container in stdout&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;docker logs -f container_name&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;tail the logs for&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2 id="host-data-with-a-docker-container"&gt;Host data with a Docker Container&lt;/h2&gt;
&lt;p&gt;Volumes are where Docker Containers can access storage (either from the Host or other Containers)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.docker.com/userguide/dockervolumes"&gt;https://docs.docker.com/userguide/dockervolumes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker run --interactive --tty --name mydata --volume /tmp/mydata:/opt/mydata trustyssh /bin/bash&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;create an interactive container named "mydata" that maps /tmp/mydata from the host onto /opt/mydata (warning: overriding any existing!)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2 id="managing-or-limiting-the-resources-available-to-a-container"&gt;Managing or limiting the resources available to a Container&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://docs.docker.com/engine/reference/run/#runtime-constraints-on-resources"&gt;https://docs.docker.com/engine/reference/run/#runtime-constraints-on-resources&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run -i -t --rm --cpuset-cpu 0 --memory 512m ubuntu:14.04 /bin/bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the usual ubuntu trusty bash prompt but anything we run will be
pinned to cpu 0 (i.e. 25% of a 4 core system) and have
at most 512 MB of RAM and 512 MB of swap available&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2 id="using-docker-for-a-gui-application"&gt;Using Docker for a GUI application&lt;/h2&gt;
&lt;p&gt;Mostly outside of the vision of containerization (neither isolation nor performance exactly) is using Docker to run GUI applications without installing them on the Host.&lt;/p&gt;
&lt;p&gt;Jessie Frazelle has done some excellent work pointing out how sharing the X11 socket from the host means lots of apps can run "without being installed" &lt;a href="https://github.com/jfrazelle/dockerfiles"&gt;https://github.com/jfrazelle/dockerfiles&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One tip she did not include was the part about XManager security, run the following if you run into an error&lt;/p&gt;
&lt;p&gt;&lt;code&gt;xhost local:root&lt;/code&gt;
- &lt;a href="https://www.netsarang.com/knowledgebase/xmanager/3898/xhost_and_how_to_use_it"&gt;https://www.netsarang.com/knowledgebase/xmanager/3898/xhost_and_how_to_use_it&lt;/a&gt;
- &lt;a href="http://www.x.org/archive/X11R6.8.0/doc/xhost.1.html"&gt;http://www.x.org/archive/X11R6.8.0/doc/xhost.1.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;xhost local:root; docker run --rm -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$DISPLAY --device /dev/snd jess/chromium&lt;/code&gt;
- modify the xhost security to allow access to x windows
- ephemeral so do not save anything
- share the X11 unix socket
- bind to the current display (i.e. :0.0)
- allow sound from the Docker container&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id="saving-a-docker-container-as-a-new-image"&gt;Saving a docker container as a new image&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;docker commit --help&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="docker-commit-for-a-container-ubuntutrusty-with-git"&gt;docker commit for a container: ubuntu:trusty with git&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;docker run -i -t ubuntu:trusty /bin/bash&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Control + p then Control + q&lt;/code&gt; &lt;em&gt;(to detach the tty without exiting the shell)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker ps -a&lt;/code&gt; &lt;em&gt;(make a note of the ID or NAME)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker attach ID_OR_NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apt-get update; apt-get install git -y&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd /root&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git --version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git clone https://johnpfeiffer@bitbucket.org/johnpfeiffer/myrepo.git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exit&lt;/code&gt;&lt;blockquote&gt;
&lt;p&gt;now the Container will have git installed, a repo cloned, and will be stopped&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;docker commit container_name_here johnpfeiffer_git_repo&lt;blockquote&gt;
&lt;p&gt;4a74440186d976caeccc52f5ed2bd44269beb84d472391a7ce26ee3db8ffc1e9&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;docker images&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;output&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;REPOSITORY               TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
johnpfeiffer_git_repo    latest              4a74440186d9        54 seconds ago      402 MB
ubuntu                   14.04               e54ca5efa2e9        3 weeks ago         276.5 MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr/&gt;
&lt;h2 id="misc"&gt;Misc&lt;/h2&gt;
&lt;h3 id="python-and-pelican-static-site-generator-on-docker-ubuntu1404"&gt;Python and Pelican static site generator on docker ubuntu:14.04&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;apt-get update; apt-get install python python-setuptools openssl wget -y&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wget -qO- https://raw.github.com/pypa/pip/master/contrib/get-pip.py | sudo python&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip install pelican Markdown beautifulsoup4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker run -i -t -p 127.0.0.1:8000:8000 --name john johnpfeiffer_git_repo:latest /bin/bash&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;control+p then control+q&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker attach john&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd /root/myrepo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pelican content  (assuming pelicanconf.py is here)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd output&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;python -m SimpleHTTPServer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;control+p then control+q&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker ps -a&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://127.0.0.1:8000&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr/&gt;
&lt;h2 id="docker-pre-built-images-from-the-registry"&gt;Docker pre built Images from the Registry&lt;/h2&gt;
&lt;h3 id="redis"&gt;Redis&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;docker search redis&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;search from the CLI but NAME, DESCRIPTION, STARS, OFFICIAL will only help you if you sort of already know what you are looking for&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://registry.hub.docker.com/_/redis/"&gt;https://registry.hub.docker.com/_/redis/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run --detach --publish 127.0.0.1:6379:6379 --name redis redis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;detached container based on the redis latest bound to the host on port 6379&lt;/p&gt;
&lt;p&gt;the image already includes by default the expose port command: EXPOSE 6379...
&lt;code&gt;apt-get install redis-tools; redis-cli&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;connect from the host to the redis container to the redis interactive cli&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;johnssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bash&lt;/span&gt;

&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;d95a758eaa6b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;
&lt;span class="n"&gt;env&lt;/span&gt;
&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;DB_PORT_6379_TCP_ADDR&lt;/span&gt;
&lt;span class="n"&gt;ping&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;PONG&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;get mykey
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;"somevalue"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;docker stop johnssh&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker start --interactive --attach johnssh&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="nv"&gt;@d95a758eaa6b&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Environment variables are tricky&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo docker run --detach --publish 127.0.0.1:2222:22 --name johnssh trustyssh&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;if you SSH into your container or use byobu "env" will not show you the info&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;**From the host: **  &lt;code&gt;sudo docker inspect --format '{{ .NetworkSettings.IPAddress }}' redis&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;172.17.0.72&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2222&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;
&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;
&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;172.17&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mykey&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;** Add a route to the Host From inside a Container/Guest **&lt;/p&gt;
&lt;p&gt;HOST:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;addr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;
&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;addr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;docker0&lt;/span&gt;
&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;addr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;docker0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;global&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;awk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;f1&lt;/span&gt;
&lt;span class="nx"&gt;HOSTIP&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;addr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;docker0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;global&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;awk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;f1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;sudo docker run -i -t --rm --add-host=docker:${HOSTIP} python:2 /bin/bash&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="mi"&gt;@1&lt;/span&gt;&lt;span class="n"&gt;bbe25092f19&lt;/span&gt;&lt;span class="o"&gt;:/&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;hosts&lt;/span&gt;

&lt;span class="mf"&gt;172.17.0.7&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;bbe25092f19&lt;/span&gt;
&lt;span class="mf"&gt;127.0.0.1&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ip6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ip6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;loopback&lt;/span&gt;
&lt;span class="n"&gt;fe00&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ip6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;localnet&lt;/span&gt;
&lt;span class="n"&gt;ff00&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ip6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mcastprefix&lt;/span&gt;
&lt;span class="n"&gt;ff02&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ip6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;allnodes&lt;/span&gt;
&lt;span class="n"&gt;ff02&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ip6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;allrouters&lt;/span&gt;

&lt;span class="mf"&gt;172.17.42.1&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;

&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="mi"&gt;@1&lt;/span&gt;&lt;span class="n"&gt;bbe25092f19&lt;/span&gt;&lt;span class="o"&gt;:/&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ping&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;

&lt;span class="n"&gt;PING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;172.17.42.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;
&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;172.17.42.1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.158&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://docs.docker.com/engine/reference/commandline/run/"&gt;https://docs.docker.com/engine/reference/commandline/run/&lt;/a&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id="docker-compose"&gt;Docker Compose&lt;/h2&gt;
&lt;p&gt;Complex real systems have multiple dependencies and following the recommended Docker pattern of "do one thing per container" means needing a way to start/orchestrate a bunch of things at once.&lt;/p&gt;
&lt;p&gt;While there are some amazing open source projects (&lt;a href="http://kubernetes.io/"&gt;http://kubernetes.io/&lt;/a&gt; , &lt;a href="https://mesos.apache.org/documentation/latest/docker-containerizer/"&gt;https://mesos.apache.org/documentation/latest/docker-containerizer/&lt;/a&gt;) it is instructive to start with the simplest model provided directly from Docker, &lt;strong&gt;&lt;a href="https://docs.docker.com/compose/"&gt;https://docs.docker.com/compose/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Either download the docker-compose binary directly (to /usr/local/bin):
- &lt;a href="https://github.com/docker/compose/releases"&gt;https://github.com/docker/compose/releases&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo curl -L "https://github.com/docker/compose/releases/download/1.25.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose&lt;/code&gt;
&lt;code&gt;sudo chmod +x /usr/local/bin/docker-compose&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Or use pip/python to install it...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo pip install --upgrade docker-compose
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;which docker-compose ; sudo docker-compose --version&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="apppy"&gt;app.py&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;redis&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'redis'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;incr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'hits'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'Hello World! I have been seen &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; times.'&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'hits'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="requirementstxt"&gt;requirements.txt&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;flask
redis
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="dockerfile"&gt;Dockerfile&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;FROM python:2.7
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD python app.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="docker-composeyml"&gt;docker-compose.yml&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5000:5000"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.:/&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;links&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;
&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;dependencies like redis are "linked" for network access (ordering in the file is important)&lt;/li&gt;
&lt;li&gt;"web" app (which comes from the current directory Dockerfile)&lt;/li&gt;
&lt;li&gt;ports: exposes ports to the host (all of these are just like the docker run CLI parameters)&lt;/li&gt;
&lt;li&gt;volumes: allows live editing of app.py&lt;/li&gt;
&lt;li&gt;redis comes from the public docker registry image (not a Dockerfile nor a private registry) and is using the default tag of "latest"&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="docker-compose-up"&gt;docker-compose up&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;docker-compose up&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;this will docker pull redis and build from the Dockerfile and then start them all in the correct order&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;docker-compose ps&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;run this command in the directory with the docker-compose.yml to see the state of the system&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;         Name                      Command             State           Ports          
-------------------------------------------------------------------------------------
composeexample_redis_1   /entrypoint.sh redis-server   Up      6379/tcp               
composeexample_web_1     /bin/sh -c python app.py      Up      0.0.0.0:5000-&amp;gt;5000/tcp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;docker ps -a&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker-compose stop&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://localhost:5000/"&gt;http://localhost:5000/&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hello World! I have been seen 2 times.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://bitbucket.org/johnpfeiffer/docker/src"&gt;https://bitbucket.org/johnpfeiffer/docker/src&lt;/a&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting&lt;/h2&gt;
&lt;h3 id="building-images-with-dockerfile"&gt;Building images with Dockerfile&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Building images often depends on the network and DNS&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you are using Wifi be careful as intermittent network connectivity may cause frustrating issues.&lt;/p&gt;
&lt;p&gt;For DNS with docker installed onto ubuntu via apt-get, try changing to Google DNS by uncommenting in:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vi /etc/default/docker&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo service docker restart&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Building images often depends on dependencies&lt;/strong&gt;
look closely at error messages, i.e. make: not found and ensure that an early RUN statement has apt-get update &amp;amp;&amp;amp; apt-get install -y build-essential&lt;/p&gt;
&lt;h3 id="discovering-an-image-parent-from-history"&gt;Discovering an Image Parent from History&lt;/h3&gt;
&lt;p&gt;To see all of the images in detail: &lt;code&gt;docker images --all --digests=true&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python      2.7-alpine      sha256:834b44717d3928266472066d82208241a2582ce7b3787f32abbfb0def9fb0324   c80455665c57        6 weeks ago         71.49 MB
alpine      latest          sha256:dfbd4a3a8ebca874ebd2474f044a0b33600d4523d03b0df76e5c5986cb02d7e8   88e169ea8f46        6 weeks ago         3.984 MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And then you can brute force query to see if an image has child dependencies:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;for i in $(docker images -q); do docker history $i | grep -q 7fb9bd20d612 &amp;amp;&amp;amp; echo $i; done&lt;/code&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id="more-info"&gt;More Info&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Real World example of using Docker for behind the firewall delivery: &lt;a href="https://bitbucket.org/atlassianlabs/ac-koa-hipchat-sassy/pull-request/6/readmemd-contains-instructions-on-how-to/diff"&gt;https://bitbucket.org/atlassianlabs/ac-koa-hipchat-sassy/pull-request/6/readmemd-contains-instructions-on-how-to/diff&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/wsargent/docker-cheat-sheet"&gt;https://github.com/wsargent/docker-cheat-sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://jpetazzo.github.io/2014/06/23/docker-ssh-considered-evil"&gt;http://jpetazzo.github.io/2014/06/23/docker-ssh-considered-evil&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="docker-api-and-the-docker-hub-public-registry"&gt;Docker API and the Docker Hub Public Registry&lt;/h3&gt;
&lt;p&gt;Docker used to have an API endpoint at registry.hub.docker.com/v1 but in a fairly typical move for them they changed it so a lot of internet "documentation" examples are wrong.&lt;/p&gt;
&lt;p&gt;Also the Docker Hub was deprecated as of 1.7 so this is the last docs on how to use it (because the service is still running or compatible)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;curl https://index.docker.io/v1/_ping
true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It uses basic authentication and while you can use the API to sign up it may just be easier to use the web site: &lt;a href="https://hub.docker.com/"&gt;https://hub.docker.com/&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nl"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="nv"&gt;@index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="ss"&gt;"OK"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;just curl with basic auth in order to check the credentials, the trailing slash is IMPORTANT &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Using a browser with the Docker REST API is often more convenient as it caches the Basic Authentication&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/v1.7/reference/api/docker-io_api/#users"&gt;https://docs.docker.com/v1.7/reference/api/docker-io_api/#users&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/v1.6/reference/api/registry_api/"&gt;https://docs.docker.com/v1.6/reference/api/registry_api/&lt;/a&gt; (because the registry api was deprecated earlier?)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To see all of the images for a given repository (it is json formatted and there will be a lot of results!)
    curl https://username:password@index.docker.io/v1/repositories/python/images&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.docker.com/v1.6/reference/api/registry_api/"&gt;https://docs.docker.com/v1.6/reference/api/registry_api/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you &lt;code&gt;docker search python&lt;/code&gt; and want to see the tags (i.e. you do not want to pull every python image ever made), then try curl with REST:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nl"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="nv"&gt;@index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;repositories&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;{"layer": "a2db1214", "name": "latest"}, {"layer": "edb21ec7", "name": "2"}, {"layer": "82b600dd", "name": "2-alpine"}, {"layer": "6a4e9662", "name": "2-onbuild"}, {"layer": "99b38a11", "name": "2-slim"}, {"layer": "fe724fa0", "name": "2-wheezy"}, {"layer": "edb21ec7", "name": "2.7"}, {"layer": "82b600dd", "name": "2.7-alpine"}, {"layer": "6a4e9662", "name": "2.7-onbuild"}, {"layer": "99b38a11", "name": "2.7-slim"}, {"layer": "fe724fa0", "name": "2.7-wheezy"}, {"layer": "c71c2739", "name": "2.7.10"}, {"layer": "f1f35fa4", "name": "2.7.10-onbuild"}, {"layer": "843123ac", "name": "2.7.10-slim"}, {"layer": "fde41dc3", "name": "2.7.10-wheezy"}, {"layer": "edb21ec7", "name": "2.7.11"}, {"layer": "82b600dd", "name": "2.7.11-alpine"}, {"layer": "6a4e9662", "name": "2.7.11-onbuild"}, {"layer": "99b38a11", "name": "2.7.11-slim"}, {"layer": "fe724fa0", "name": "2.7.11-wheezy"}, {"layer": "a87a2288", "name": "2.7.7"}, {"layer": "481b175a", "name": "2.7.8"}, {"layer": "fbb30ed2", "name": "2.7.8-onbuild"}, {"layer": "3cf7f142", "name": "2.7.8-slim"}, {"layer": "6a873836", "name": "2.7.8-wheezy"}, {"layer": "2d0d0130", "name": "2.7.9"}, {"layer": "10948f7c", "name": "2.7.9-onbuild"}, {"layer": "e86252d0", "name": "2.7.9-slim"}, {"layer": "a11d441b", "name": "2.7.9-wheezy"}, {"layer": "a2db1214", "name": "3"}, {"layer": "bb6cd371", "name": "3-alpine"}, {"layer": "80662aa6", "name": "3-onbuild"}, {"layer": "07bfefb9", "name": "3-slim"}, {"layer": "2edf9614", "name": "3-wheezy"}, {"layer": "7575f4a5", "name": "3.2"}, {"layer": "31b273f6", "name": "3.2-onbuild"}, {"layer": "ca0a0ed6", "name": "3.2-slim"}, {"layer": "f5644650", "name": "3.2-wheezy"}, {"layer": "7575f4a5", "name": "3.2.6"}, {"layer": "31b273f6", "name": "3.2.6-onbuild"}, {"layer": "ca0a0ed6", "name": "3.2.6-slim"}, {"layer": "f5644650", "name": "3.2.6-wheezy"}, {"layer": "84717b99", "name": "3.3"}, {"layer": "4de0c1a0", "name": "3.3-alpine"}, {"layer": "e0985e72", "name": "3.3-onbuild"}, {"layer": "3c0f39af", "name": "3.3-slim"}, {"layer": "a13ad718", "name": "3.3-wheezy"}, {"layer": "e663e96e", "name": "3.3.5"}, {"layer": "79d3367e", "name": "3.3.5-onbuild"}, {"layer": "84717b99", "name": "3.3.6"}, {"layer": "4de0c1a0", "name": "3.3.6-alpine"}, {"layer": "e0985e72", "name": "3.3.6-onbuild"}, {"layer": "3c0f39af", "name": "3.3.6-slim"}, {"layer": "a13ad718", "name": "3.3.6-wheezy"}, {"layer": "c7184f4f", "name": "3.4"}, {"layer": "e6310f15", "name": "3.4-alpine"}, {"layer": "c38d9f7b", "name": "3.4-onbuild"}, {"layer": "ab9f7f65", "name": "3.4-slim"}, {"layer": "c98c4a9d", "name": "3.4-wheezy"}, {"layer": "b504e00c", "name": "3.4.1"}, {"layer": "07e5901a", "name": "3.4.1-onbuild"}, {"layer": "ec50e6a0", "name": "3.4.2"}, {"layer": "ade8543e", "name": "3.4.2-onbuild"}, {"layer": "dd1dee45", "name": "3.4.2-slim"}, {"layer": "de6911d6", "name": "3.4.2-wheezy"}, {"layer": "48bc52cc", "name": "3.4.3"}, {"layer": "bf599bc6", "name": "3.4.3-onbuild"}, {"layer": "0b92f173", "name": "3.4.3-slim"}, {"layer": "b8845e5b", "name": "3.4.3-wheezy"}, {"layer": "c7184f4f", "name": "3.4.4"}, {"layer": "e6310f15", "name": "3.4.4-alpine"}, {"layer": "c38d9f7b", "name": "3.4.4-onbuild"}, {"layer": "ab9f7f65", "name": "3.4.4-slim"}, {"layer": "c98c4a9d", "name": "3.4.4-wheezy"}, {"layer": "a2db1214", "name": "3.5"}, {"layer": "bb6cd371", "name": "3.5-alpine"}, {"layer": "80662aa6", "name": "3.5-onbuild"}, {"layer": "07bfefb9", "name": "3.5-slim"}, {"layer": "c64596cb", "name": "3.5.0"}, {"layer": "c9744d7e", "name": "3.5.0-onbuild"}, {"layer": "ac60c7d8", "name": "3.5.0-slim"}, {"layer": "31ef8f64", "name": "3.5.0b3"}, {"layer": "7c5e081c", "name": "3.5.0b3-onbuild"}, {"layer": "0c47d2de", "name": "3.5.0b3-slim"}, {"layer": "a2db1214", "name": "3.5.1"}, {"layer": "bb6cd371", "name": "3.5.1-alpine"}, {"layer": "80662aa6", "name": "3.5.1-onbuild"}, {"layer": "07bfefb9", "name": "3.5.1-slim"}, {"layer": "bb6cd371", "name": "alpine"}, {"layer": "80662aa6", "name": "onbuild"}, {"layer": "07bfefb9", "name": "slim"}, {"layer": "2edf9614", "name": "wheezy"}&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="docker-engine-internal-api"&gt;Docker Engine internal API&lt;/h4&gt;
&lt;p&gt;If instead of the docker client you wish to interact more programatically...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/reference/api/docker_remote_api/"&gt;https://docs.docker.com/engine/reference/api/docker_remote_api/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/docker/distribution"&gt;https://github.com/docker/distribution&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="private-docker-registry"&gt;Private Docker Registry&lt;/h3&gt;
&lt;h4 id="login-to-a-private-docker-registry"&gt;Login to a private docker registry&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nl"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="nv"&gt;@docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;attempt to login to a private registry&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;** Using the docker client to login to a private registry **&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;com&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="nv"&gt;@example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;WARNING&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;saved&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Login&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Succeeded&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="private-registry-basics-via-the-browser"&gt;private registry basics via the browser&lt;/h4&gt;
&lt;p&gt;How to deploy your own docker registry: &lt;a href="https://docs.docker.com/registry/deploying/"&gt;https://docs.docker.com/registry/deploying/&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://docker.example.com/_ping
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;{}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://docker.example.com/info
https://docker.example.com/version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;note these commands may not be enabled or available in your private registry version &lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//docker.example.com/v1/repositories/library/ubuntu/tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;{"13.04": "5e47ac691989afcd10285ea4e67b46bc0fdc98d90844e57a6d4221c1e3ab4388"}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://docker.example.com/v1/repositories/micros/baseimage-ubuntu/tags
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;{"latest": "5a14c1498ff4983793f6e5eddd16868dbad257195f0e85c66ece94d881ecb28f"}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://docker.example.com/v1/repositories/micros/baseimage-ubuntu/images
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;list of the images available: [{"id":"8254ff58b098b72425854555204171352a69f5427ba83dee4642ba45d301d0b1"}]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://docker.example.com/v1/repositories/micros/baseimage-ubuntu/json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;inspect an image (what OS, kernel, etc.) {"arch": "amd64", "docker_go_version": "go1.3.3", "docker_version": "1.3.3", "kernel": "3.16.7-tinycore64", "last_update": 1426041024, "os": "linux"}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://docker.example.com/v1/repositories/myuser/nginx/0348bf1e7cc54327b8c9ce8407c5b3eadade1ef1771d642d08ae16a6aad5bed5/json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;inspect a very specific image (by id)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="searching-a-private-docker-registry"&gt;Searching a private docker registry&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nl"&gt;https:&lt;/span&gt;&lt;span class="c1"&gt;//registry.hub.docker.com/v1/search?q=pfeiffer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the public docker registry search query, deprecated in APIv2 so no longer functional&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker search docker.example.com/myuser
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the cli command returns a listing of all of the images for a user, deprecated in APIv2 so no longer functional&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If there is a proxy in front: &lt;code&gt;docker search user:password@docker.example.com/myuser&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="nv"&gt;@docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;search&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;LIST ALL IMAGES: or use a browser https://docker.example.com/v1/search&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="nv"&gt;@docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;search&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ubuntu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;https://docker.example.com/v1/search?q=ubuntu
{"num_results": 4, "query": "ubuntu", "results": [{"description": null, "name": "example/ubuntu"}, {"description": null, "name": "library/ubuntu"}, {"description": null, "name": "micros/baseimage-ubuntu-ansible"}, {"description": null, "name": "micros/baseimage-ubuntu"}]}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-private-docker-registry-on-ubuntu-14-04"&gt;https://www.digitalocean.com/community/tutorials/how-to-set-up-a-private-docker-registry-on-ubuntu-14-04&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="virtualization"/><category term="docker"/><category term="containers"/></entry><entry><title>Markdown syntax cheatsheet</title><link href="https://blog.john-pfeiffer.com/markdown-syntax-cheatsheet/" rel="alternate"/><published>2014-07-02T20:21:00-07:00</published><updated>2014-07-02T20:21:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-07-02:/markdown-syntax-cheatsheet/</id><summary type="html">
&lt;h3 id="markdown-syntax"&gt;Markdown Syntax&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;html &amp;lt;em&amp;gt; is markdown &lt;code&gt;*emphasis* or _italics_&lt;/code&gt; = &lt;em&gt;emphasis&lt;/em&gt; or &lt;em&gt;italics&lt;/em&gt; &lt;/li&gt;
&lt;li&gt;html &amp;lt;strong&amp;gt; is markdown &lt;code&gt;**strong* or __bold__&lt;/code&gt; = &lt;strong&gt;strong&lt;/strong&gt; or &lt;strong&gt;bold&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;html strikethrough is not supported but can just be &lt;code&gt;&amp;lt;del&amp;gt;strikethrough&amp;lt;/del&amp;gt;&lt;/code&gt; = &lt;del&gt;strikethrough&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;html &amp;lt;blockquote&amp;gt; is markdown &lt;code&gt;&amp;gt;&lt;/code&gt;&lt;blockquote&gt;
&lt;p&gt;at the start of each line&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;html unordered list &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; = &lt;code&gt;- item …&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">
&lt;h3 id="markdown-syntax"&gt;Markdown Syntax&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;html &amp;lt;em&amp;gt; is markdown &lt;code&gt;*emphasis* or _italics_&lt;/code&gt; = &lt;em&gt;emphasis&lt;/em&gt; or &lt;em&gt;italics&lt;/em&gt; &lt;/li&gt;
&lt;li&gt;html &amp;lt;strong&amp;gt; is markdown &lt;code&gt;**strong* or __bold__&lt;/code&gt; = &lt;strong&gt;strong&lt;/strong&gt; or &lt;strong&gt;bold&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;html strikethrough is not supported but can just be &lt;code&gt;&amp;lt;del&amp;gt;strikethrough&amp;lt;/del&amp;gt;&lt;/code&gt; = &lt;del&gt;strikethrough&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;html &amp;lt;blockquote&amp;gt; is markdown &lt;code&gt;&amp;gt;&lt;/code&gt;&lt;blockquote&gt;
&lt;p&gt;at the start of each line&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;html unordered list &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; = &lt;code&gt;- item&lt;/code&gt; or alternatives: &lt;code&gt;+ item&lt;/code&gt; , &lt;code&gt;* item&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;html &amp;lt;hr /&amp;gt; is markdown &lt;code&gt;- - -&lt;/code&gt; or alternatives: &lt;code&gt;* * *&lt;/code&gt; , &lt;code&gt;***&lt;/code&gt; , &lt;code&gt;*****&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;html &amp;lt;a href= for hyper links is:&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;http://blog.john-pfeiffer.com&amp;gt;&lt;/code&gt; converts into a link that is automatically turned clickable:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;a href="http://johnpfeiffer.bitbucket.org"&amp;gt;http://johnpfeiffer.bitbucket.org&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[an example](http://example.com/ "ExampleTitle")&lt;/code&gt; &lt;a href="/about-john-pfeiffer" title="ExampleTitle"&gt;an example&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;both absolute and relative links are supported, as well as reference links that are defined elsewhere:
&lt;code&gt;This is [an example][someid]&lt;/code&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;[someid]: http://example.com/  "Optional Title Here"&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li&gt;numbered list &lt;code&gt;1&lt;/code&gt; at the beginning of each line&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;any digit will do, the numbering is rendered in order&lt;/li&gt;
&lt;li&gt;ensure the numbered list is surrounded by empty lines&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Inline code&lt;/strong&gt; is markdown `backtick around the text` = &lt;code&gt;backtick around the text&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A code block&lt;/strong&gt; is markdown &lt;code&gt;indent 4 spaces or 1 tab&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;a blank line in the code block still needs to be indented&lt;/li&gt;
&lt;li&gt;ensure the code block is surrounded by empty lines&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;:::text or :::bash at the top of a code block will control the syntax highlighting, see &lt;a href="http://pygments.org/docs/lexers"&gt;http://pygments.org/docs/lexers&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="h1"&gt;H1&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;# H1&lt;/code&gt;&lt;/p&gt;
&lt;h6 id="h6"&gt;H6&lt;/h6&gt;
&lt;p&gt;&lt;code&gt;###### H6&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="tables-are-sometimes-not-supported-but"&gt;Tables are (sometimes) not supported but...&lt;/h3&gt;
&lt;h4 id="table-with-left-justified-github-flavored-markdown"&gt;Table with left justified (GitHub Flavored Markdown)&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;|in|out|other|
|---|---|---|
|yes|no|maybe|
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr/&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;in&lt;/th&gt;
&lt;th&gt;out&lt;/th&gt;
&lt;th&gt;other&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;maybe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*emphasis*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;emphasis&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h4 id="table-with-text-center-aligned"&gt;Table with text center aligned&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;|short|long centered|
|:-:|:-:|
|y|n|
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr/&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;short&lt;/th&gt;
&lt;th style="text-align: center;"&gt;long centered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;y&lt;/td&gt;
&lt;td style="text-align: center;"&gt;n&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h4 id="html-table"&gt;HTML Table&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;header&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;first column in row 1&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;2nd column&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr/&gt;
&lt;h3 id="more-info"&gt;more info&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://daringfireball.net/projects/markdown/syntax"&gt;http://daringfireball.net/projects/markdown/syntax&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="markdown"/><category term="html"/></entry><entry><title>Creating a static web site with Bitbucket</title><link href="https://blog.john-pfeiffer.com/creating-a-static-web-site-with-bitbucket/" rel="alternate"/><published>2014-06-30T20:20:00-07:00</published><updated>2014-06-30T20:20:00-07:00</updated><author><name>John Pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-06-30:/creating-a-static-web-site-with-bitbucket/</id><summary type="html">&lt;p&gt;How to host a static web site on Bitbucket for free&lt;/p&gt;</summary><content type="html">&lt;ol&gt;
&lt;li&gt;create a bitbucket account with username&lt;/li&gt;
&lt;li&gt;create a repo named username.bitbucket.org&lt;/li&gt;
&lt;li&gt;mkdir username.bitbucket.org&lt;/li&gt;
&lt;li&gt;cd username.bitbucket.org&lt;/li&gt;
&lt;li&gt;echo "hi" &amp;gt; index.html&lt;/li&gt;
&lt;li&gt;git init .&lt;/li&gt;
&lt;li&gt;git add&lt;/li&gt;
&lt;li&gt;git commit -m "first site index"&lt;/li&gt;
&lt;li&gt;git remote add origin git@bitbucket.org:username/username.bitbucket.org.git&lt;/li&gt;
&lt;li&gt;git push origin master&lt;/li&gt;
&lt;li&gt;git branch --set-upstream master origin/master&lt;/li&gt;
&lt;li&gt;git pull&lt;/li&gt;
&lt;li&gt;https://username.bitbucket.org&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href="https://confluence.atlassian.com/display/BITBUCKET/Publishing+a+Website+on+Bitbucket"&gt;https://confluence.atlassian.com/display/BITBUCKET/Publishing+a+Website+on+Bitbucket&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Scroll down to the comments:
&lt;em&gt;You can setup a CNAME for your account .  You cannot set up a CNAME for a static website hosted on Bitbucket.  The account you setup a CNAME for may have a repository that represents a static website.&lt;/em&gt;&lt;/p&gt;</content><category term="programming"/><category term="programming"/></entry><entry><title>How to set up a Pelican static blog site</title><link href="https://blog.john-pfeiffer.com/how-to-set-up-a-pelican-static-blog-site/" rel="alternate"/><published>2014-06-21T20:21:00-07:00</published><updated>2014-06-21T20:21:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2014-06-21:/how-to-set-up-a-pelican-static-blog-site/</id><summary type="html">
&lt;p&gt;Pelican is an open source project that converts static text files into an html site.&lt;/p&gt;
&lt;p&gt;Why use a static site generator (pelican) instead of a hosted blog platform or a CMS (Content Management System)?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Because less is more and you should use the right tool for the right job&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">
&lt;p&gt;Pelican is an open source project that converts static text files into an html site.&lt;/p&gt;
&lt;p&gt;Why use a static site generator (pelican) instead of a hosted blog platform or a CMS (Content Management System)?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Because less is more and you should use the right tool for the right job&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A static site made of html pages is very easy to maintain&lt;/li&gt;
&lt;li&gt;It is also more secure and performance is good too =)&lt;/li&gt;
&lt;li&gt;Allows for straightforward use of version control (git)&lt;/li&gt;
&lt;li&gt;Developers prefer to be able to customize and add functionality (python and javascript)&lt;/li&gt;
&lt;li&gt;Using widely adopted open source software reduces risk (more contributors, more testers, more bugfixes)&lt;/li&gt;
&lt;li&gt;Using a widely adopted platform increases leverage (themes, plugins, tutorials, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="install-pelican"&gt;Install Pelican&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;sudo pip install pelican Markdown beautifulsoup4&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;installing both the pelican and the Markdown packages&lt;/li&gt;
&lt;li&gt;beautifulsoup4 is a dependency for the later step of the elegant theme TOC and search plugins&lt;/li&gt;
&lt;li&gt;optionally use virtualenv venv; source venv/bin/activate&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;pelican-quickstart&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Welcome to pelican-quickstart v3.3.0.
This script will help you create a new Pelican-based website.
Please answer the following questions so this script can generate the files needed by Pelican.

&amp;gt; Where do you want to create your new web site? [.]
&amp;gt; What will be the title of this web site? johnpfeiffer.bitbucket.io
&amp;gt; Who will be the author of this web site? john pfeiffer
&amp;gt; What will be the default language of this web site? [en]
&amp;gt; Do you want to specify a URL prefix? e.g., http://example.com   (Y/n)
&amp;gt; What is your URL prefix? (see above example; no trailing slash) https://johnpfeiffer.bitbucket.io
&amp;gt; Do you want to enable article pagination? (Y/n)
&amp;gt; Do you want to generate a Fabfile/Makefile to automate generation and publishing? (Y/n)
&amp;gt; Do you want an auto-reload &amp;amp; simpleHTTP script to assist with theme and site development? (Y/n)
&amp;gt; Do you want to upload your website using FTP? (y/N)
&amp;gt; Do you want to upload your website using SSH? (y/N)
&amp;gt; Do you want to upload your website using Dropbox? (y/N)
&amp;gt; Do you want to upload your website using S3? (y/N)
&amp;gt; Do you want to upload your website using Rackspace Cloud Files? (y/N)
Done. Your new project is available at /home/ubuntu/BLOG
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr/&gt;
&lt;p&gt;&lt;code&gt;tree&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;.
├── content
├── develop_server.sh
├── fabfile.py
├── Makefile
├── output
├── pelicanconf.py
└── publishconf.py

2 directories, 5 files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr/&gt;
&lt;h3 id="create-content"&gt;Create Content&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;vi content/hello_world.md&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Title: My first blog post
Date: 2014-06-21 20:20
Tags: python
Slug: my-first-blog-post
Author: John Pfeiffer
Summary: Short version for index and feeds

This is the content of my first blog post.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;optional UI markdown editor: 
&lt;code&gt;sudo apt-get install retext&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h3 id="run-a-dev-server-to-see-the-results-locally"&gt;Run a dev server to see the results locally&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;make devserver&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;...Starting up Pelican and pelican.server...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;./develop_server.sh stop&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;stop the dev server (required if reloading the .conf file)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;This only works with the basic first setup, after that it is better to manually use multiple screens:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;make clean&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;make regenerate&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;auto detects any content changes and reloads itself&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cd output; python -m SimpleHTTPServer&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Serving HTTP on 0.0.0.0 port 8000 ... (Control + C to quit)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;http://localhost:8000&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://docs.getpelican.com/en/latest/publish.html"&gt;http://docs.getpelican.com/en/latest/publish.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="setup-a-static-pages-directory"&gt;Setup a static Pages directory&lt;/h4&gt;
&lt;p&gt;Besides lots of articles in categories it can be useful to have a few pages like About or Contact
    mkdir -p content/pages
    echo "Title: About Us" &amp;gt; content/pages/about.md&lt;/p&gt;
&lt;p&gt;&lt;a href="http://docs.getpelican.com/en/latest/content.html#pages"&gt;http://docs.getpelican.com/en/latest/content.html#pages&lt;/a&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h3 id="publish"&gt;Publish&lt;/h3&gt;
&lt;p&gt;I just use the pelicanconf output rather than publishconf, and I use git with a bitbucket static html site.&lt;/p&gt;
&lt;hr/&gt;
&lt;h3 id="example-pelican-configuration-file"&gt;Example pelican configuration file&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Contains the elegant theme and tipue search plugin&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vi pelicanconf.py&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#!/usr/bin/env python
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals

AUTHOR = u'john pfeiffer'
SITENAME = u'johnpfeiffer'
SITEURL = u'https://blog-john-pfeiffer.com'
OUTPUT_PATH = 'output/'
DEFAULT_DATE_FORMAT = '%Y-%m-%d'
TIMEZONE="America/Los_Angeles"

# Feed generation is usually not desired when developing
FEED_ALL_ATOM = None
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None

# clean urls for pages , trailing slash to support HTTPS
PAGE_URL = '{slug}/'
PAGE_SAVE_AS = '{slug}/index.html'
# clean urls for articles
ARTICLE_SAVE_AS = '{slug}/index.html'
ARTICLE_URL = '{slug}/'

DEFAULT_PAGINATION = 10

THEME = 'themes/pelican-elegant'
PLUGIN_PATHS = ['plugins']
PLUGINS = ['sitemap', 'extract_toc', 'tipue_search', 'post_stats']
MD_EXTENSIONS = ['codehilite(css_class=highlight)', 'extra', 'headerid', 'toc']
DIRECT_TEMPLATES = (('index', 'tags', 'categories','archives', 'search', '404'))
STATIC_PATHS = ['theme/images', 'images']

SITEMAP = {
    'format': 'xml',
    'priorities': {
        'articles': 0.5,
        'indexes': 0.5,
        'pages': 0.5
     },
    'changefreqs': {
        'articles': 'monthly',
        'indexes': 'daily',
        'pages': 'monthly'
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Hint: if you use SSL (e.g. cloudflare) then make sure your SITEURL is https and there are trailing slashes on your clean url helpers PAGE_URL and ARTICLE_URL otherwise you may get mixed content warnings or mixed http and https links in your output html&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cp -a pelicanconf.py publishconf.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Update publishconf.py with any final tweaks that should occur only in Production&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h3 id="pelican-themes"&gt;Pelican Themes&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/getpelican/pelican-themes"&gt;https://github.com/getpelican/pelican-themes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git clone --recursive https://github.com/getpelican/pelican-themes ~/pelican-themes&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://pelican.readthedocs.org/en/latest/pelican-themes.html"&gt;http://pelican.readthedocs.org/en/latest/pelican-themes.html&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir themes
cp -a pelican-themes/elegant themes/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This is the lazy way, the correct way is to only copy in the theme you are using ;)&lt;/p&gt;
&lt;p&gt;FYI For the elegant with tipuesearch you need to also install the tipuesearch plugin&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://moparx.com/2014/04/adding-search-capabilities-within-your-pelican-powered-site-using-tipue-search/"&gt;http://moparx.com/2014/04/adding-search-capabilities-within-your-pelican-powered-site-using-tipue-search/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="depedency-management-by-vendoring-in-version-control"&gt;Depedency Management by Vendoring in Version Control&lt;/h3&gt;
&lt;p&gt;Dependencies are always hard and leveraging the work of others (themes and plugins) means you have to consider how to manage changes to those moving parts too.&lt;/p&gt;
&lt;p&gt;Using your Build/CI/CD server to grab the latest themes/plugins is a mistake because while you would benefit from any new features and bug fixes you will also get burned (yes it has happened to me!) when you are publishing a blog post and suddenly your site doesn't work.  &lt;/p&gt;
&lt;p&gt;(Oh are you using Continuous Post Deploy Smoke Tests and Realtime Everything Monitoring to detect this on your blog?)&lt;/p&gt;
&lt;p&gt;I highly recommend "vendoring" by copying pelican-elegant into the themes subdirectory in your version control.&lt;/p&gt;
&lt;p&gt;This sort of pinning makes an explicit choice that will ensure your site will continue to work until you choose to next upgrade.  Pinning in the Build/CI/CD server by git sha will also work but makes it harder to keep everything in one place for development.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The drawback of "vendoring" is the technical debt by the inevitable customization that occurs in this "directory separated" fork of the original&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h3 id="pelican-plugins"&gt;Pelican Plugins&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/getpelican/pelican-plugins"&gt;https://github.com/getpelican/pelican-plugins&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git clone https://github.com/getpelican/pelican-plugins
mkdir plugins
cp -a pelican-plugins/sitemap plugins/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;note this is the lazy way, a more precise way would be to only copy in the plugins used&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="hacking-listjs-and-per-article-word-count-into-a-theme"&gt;Hacking ListJS and per Article word count into a theme&lt;/h3&gt;
&lt;p&gt;Basically once you get the hang of the config file format (just python really) and the templating engine (HTML with some jinja2) you can add all sorts of interesting pieces.&lt;/p&gt;
&lt;p&gt;In this example I leverage some javascript and another pelican plugin (python) to allow sorting and filtering on more data than the usual "archives.html" provides.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"article-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sort"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;data-sort=&lt;/span&gt;&lt;span class="s"&gt;"date"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;date&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sort"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;data-sort=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;title&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sort"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;data-sort=&lt;/span&gt;&lt;span class="s"&gt;"wordcount"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;word&lt;span class="w"&gt; &lt;/span&gt;count&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sort"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;data-sort=&lt;/span&gt;&lt;span class="s"&gt;"category"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;category&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Find by filter"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin-top: 10px; height: 16px;"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;nbsp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;dates&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;articles

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;category&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;articles&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;categories&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;article&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;articles&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"date"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"padding-right: 10px;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;pubdate=&lt;/span&gt;&lt;span class="s"&gt;"pubdate"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;datetime=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.date.isoformat&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.locale_date&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/time&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;SITEURL&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.url&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;span&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.title&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;article.subtitle&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;small&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.subtitle&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;em&amp;gt;&amp;lt;span&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wordcount"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;(&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.stats&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'wc'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;words)&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/em&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;em&amp;gt;&amp;lt;span&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"category"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.category&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/em&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
...
...
&lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;var&lt;span class="w"&gt; &lt;/span&gt;options&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt;        &lt;/span&gt;valueNames:&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="w"&gt; &lt;/span&gt;'date',&lt;span class="w"&gt; &lt;/span&gt;'title',&lt;span class="w"&gt; &lt;/span&gt;'wordcount',&lt;span class="w"&gt; &lt;/span&gt;'category'],
&lt;span class="w"&gt;        &lt;/span&gt;page:&lt;span class="w"&gt; &lt;/span&gt;1000
&lt;span class="w"&gt;    &lt;/span&gt;};
&lt;span class="w"&gt;    &lt;/span&gt;var&lt;span class="w"&gt; &lt;/span&gt;hackerList&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;List('article-list',&lt;span class="w"&gt; &lt;/span&gt;options);
&lt;span class="w"&gt;    &lt;/span&gt;hackerList.sort('date',&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;order:&lt;span class="w"&gt; &lt;/span&gt;"desc"&lt;span class="w"&gt; &lt;/span&gt;})
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/listjs-sort-filters-search-and-more-for-html-lists-and-tables-in-javascript/"&gt;https://blog.john-pfeiffer.com/listjs-sort-filters-search-and-more-for-html-lists-and-tables-in-javascript/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/getpelican/pelican-plugins/tree/master/post_stats"&gt;https://github.com/getpelican/pelican-plugins/tree/master/post_stats&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="advanced-skipping-the-makefile"&gt;Advanced: skipping the Makefile&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;pelican&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;help&lt;/span&gt;
&lt;span class="nx"&gt;pelican&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;publishconf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;py&lt;/span&gt;
&lt;span class="nx"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SimpleHTTPServer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr/&gt;
&lt;h3 id="importing-from-drupal-with-pelican-import"&gt;Importing from Drupal with pelican-import&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Hack the Drupal files to allow a lot more than 10 items per feed&lt;/strong&gt;
&lt;code&gt;grep -r 'items per feed' .&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;learned from drupal-7.28/modules/system/system.module&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;vi modules/system/system.admin.inc&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$form['feed_default_items']&lt;/p&gt;
&lt;p&gt;Add to the dropdown choices of 10, 15, 30 etc. the option of 999&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pandoc&lt;/span&gt;
&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;feedparser&lt;/span&gt;
&lt;span class="n"&gt;pelican&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;
&lt;span class="n"&gt;pelican&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;markdown&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="more-info"&gt;more info&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pelican.readthedocs.org/en/latest/settings.html"&gt;https://pelican.readthedocs.org/en/latest/settings.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Tweaking default syntax highlighting:&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://pygments.org/docs/lexers"&gt;http://pygments.org/docs/lexers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://pygments.org/demo"&gt;http://pygments.org/demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bitbucket.org/johnpfeiffer/docker/src"&gt;https://bitbucket.org/johnpfeiffer/docker/src&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="programming"/><category term="python"/><category term="pelican"/><category term="blog"/><category term="static site"/></entry><entry><title>Cumulus compatible S3, nginx, and HMAC signed requests</title><link href="https://blog.john-pfeiffer.com/cumulus-compatible-s3-nginx-and-hmac-signed-requests/" rel="alternate"/><published>2013-11-26T17:10:00-08:00</published><updated>2013-11-26T17:10:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2013-11-26:/cumulus-compatible-s3-nginx-and-hmac-signed-requests/</id><summary type="html">&lt;p&gt;With the exceptionally fast, reliable and popular web server &lt;strong&gt;&lt;a href="http://nginx.org/"&gt;nginx&lt;/a&gt;&lt;/strong&gt; as a front end customers can use a browser to access their uploaded files via a simple URL, the same as the SaaS Amazon S3 implementation, without knowing about the &lt;strong&gt;&lt;a href="http://www.nimbusproject.org/doc/nimbus/faq/#what-is-cumulus"&gt;Cumulus backend&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately there were edge cases around the encodings …&lt;/p&gt;</summary><content type="html">&lt;p&gt;With the exceptionally fast, reliable and popular web server &lt;strong&gt;&lt;a href="http://nginx.org/"&gt;nginx&lt;/a&gt;&lt;/strong&gt; as a front end customers can use a browser to access their uploaded files via a simple URL, the same as the SaaS Amazon S3 implementation, without knowing about the &lt;strong&gt;&lt;a href="http://www.nimbusproject.org/doc/nimbus/faq/#what-is-cumulus"&gt;Cumulus backend&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately there were edge cases around the encodings of spaces, pluses, slashes, etc. where nginx + Cumulus was returning "Access Denied" when trying to GET a file.&lt;/p&gt;
&lt;p&gt;Examining the relevant RFC's (&lt;a href="http://tools.ietf.org/html/rfc3986#section-2.1"&gt;http://tools.ietf.org/html/rfc3986#section-2.1&lt;/a&gt;), PHP
(&lt;a href="http://php.net/manual/en/function.rawurlencode.php"&gt;http://php.net/manual/en/function.rawurlencode.php&lt;/a&gt;) and Python  ﻿(&lt;a href="http://docs.python.org/2/library/urllib.html"&gt;http://docs.python.org/2/library/urllib.html&lt;/a&gt;) references, and examining the logs, I could see the files were PUT correctly, &lt;a href="http://s3tools.org/s3cmd"&gt;s3cmd&lt;/a&gt; could retrieve the  binary objects (files) from Cumulus fine... but the logs were showing a change in the URL's.&lt;/p&gt;
&lt;p&gt;Increasing the &lt;a href="http://nginx.org/en/docs/debugging_log.html"&gt;debugging in nginx&lt;/a&gt;, digging into the &lt;a href="https://github.com/nimbusproject/nimbus/tree/master/cumulus"&gt;Cumulus source
code&lt;/a&gt; and &lt;a href="https://github.com/anomalizer/ngx_aws_auth"&gt;nginx AWS Authentication Module&lt;/a&gt; (and adding more logging statements in  Python and C respectively), I realized there was a mismatch in the REST URL signature process.&lt;/p&gt;
&lt;p&gt;Since Cumulus was using the open source &lt;a href="http://docs.pythonboto.org/en/latest/"&gt;Python Boto library&lt;/a&gt; which is actually &lt;a href="https://github.com/boto/boto/graphs/contributors"&gt;supported&lt;/a&gt; by &lt;a href="http://aws.amazon.com/sdkforpython"&gt;Amazon&lt;/a&gt; (the de facto rulers of the S3 "standard"), I decided that their &lt;a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html"&gt;signing process&lt;/a&gt; was authoritative.&lt;/p&gt;
&lt;p&gt;A lot of digging into nginx configs and source, along with learning a bit about nginx module development and hacking the source of the ngx_aws_auth module, I finally came up with a matching signature process, (success!)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ngx_aws_auth/ngx_http_aws_auth.c&lt;/strong&gt;  &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cm"&gt;/* uses the source and length to copy the uri, does not escape characters&lt;/span&gt;
&lt;span class="cm"&gt;(the argument signature is compatible with ngx_escape_uri)&lt;/span&gt;
&lt;span class="cm"&gt;*/&lt;/span&gt;
&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ngx_uri_extractor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u_char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u_char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ngx_uint_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* customized to calculate the signature using the non escaped URI, compatible with cumulus boto&lt;/span&gt;
&lt;span class="cm"&gt;*/&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ngx_int_t&lt;/span&gt;
&lt;span class="nf"&gt;ngx_http_aws_auth_variable_s3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ngx_http_request_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ngx_http_variable_value_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;ngx_http_aws_auth_conf_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;aws_conf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;md_len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;md&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;EVP_MAX_MD_SIZE&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;aws_conf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ngx_http_get_module_loc_conf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ngx_http_aws_auth_module&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/*&lt;/span&gt;
&lt;span class="cm"&gt;    * This Block of code added to deal with paths that are not on the root -&lt;/span&gt;
&lt;span class="cm"&gt;    * that is, via proxy_pass that are being redirected and the base part of&lt;/span&gt;
&lt;span class="cm"&gt;    * the proxy url needs to be taken off the beginning of the URI in order&lt;/span&gt;
&lt;span class="cm"&gt;    * to sign it correctly.&lt;/span&gt;
&lt;span class="cm"&gt;    */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;u_char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ngx_palloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// allow room for escaping&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/*&lt;/span&gt;
&lt;span class="cm"&gt;    u_char *uri_end = (u_char*) ngx_escape_uri(uri,r-&amp;gt;uri.data, r-&amp;gt;uri.len, NGX_ESCAPE_URI);&lt;/span&gt;
&lt;span class="cm"&gt;    */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;u_char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;uri_end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u_char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ngx_uri_extractor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;unparsed_uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;unparsed_uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NGX_ESCAPE_URI&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;uri_end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;\0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// null terminate&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="programming"/><category term="c"/><category term="nginx"/></entry><entry><title>Selenium headless browser automated testing with PhantomJS and Python</title><link href="https://blog.john-pfeiffer.com/selenium-headless-browser-automated-testing-with-phantomjs-and-python/" rel="alternate"/><published>2013-09-17T21:04:00-07:00</published><updated>2013-09-17T21:04:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2013-09-17:/selenium-headless-browser-automated-testing-with-phantomjs-and-python/</id><summary type="html">
&lt;h3 id="why-automated-testing"&gt;Why Automated Testing&lt;/h3&gt;
&lt;p&gt;Automated Testing is critical to maintaining quality while increasing velocity &lt;em&gt;(aka avoiding being crushed to death by technical debt).&lt;/em&gt;
Web sites are inherently complex to test:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;requires a client and server,&lt;/li&gt;
&lt;li&gt;combination of UI and Logic,&lt;/li&gt;
&lt;li&gt;lots of paths,&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A challenge facing the HipChat team is …&lt;/p&gt;</summary><content type="html">
&lt;h3 id="why-automated-testing"&gt;Why Automated Testing&lt;/h3&gt;
&lt;p&gt;Automated Testing is critical to maintaining quality while increasing velocity &lt;em&gt;(aka avoiding being crushed to death by technical debt).&lt;/em&gt;
Web sites are inherently complex to test:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;requires a client and server,&lt;/li&gt;
&lt;li&gt;combination of UI and Logic,&lt;/li&gt;
&lt;li&gt;lots of paths,&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A challenge facing the HipChat team is how to maintain quality while supporting both SaaS and BTF versions, expanding the team, increasing the functionality, and experiment with architecture (or even just normal bug fixing refactoring).&lt;/p&gt;
&lt;h3 id="choosing-selenium"&gt;Choosing Selenium&lt;/h3&gt;
&lt;p&gt;Continued innovation by the Selenium project has created a stable and usable programmatic interface such that clicking on different components in a web page can be automated.&lt;/p&gt;
&lt;p&gt;Selenium tests can be generated manually by a Firefox plugin &lt;a href="https://docs.seleniumhq.org/projects/ide/"&gt;https://docs.seleniumhq.org/projects/ide/&lt;/a&gt; and then saved to a .html file which is helpful in that it can be done by non programmers. Eessentially selenium as a Domain Specific Language creates artifacts rather than tribal knowledge.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The Selenium Documentation is overly complicated by legacy versions just use the IDE, play with the record button and click on stuff, save the .html, and analyze and you'll quickly get the hang of it.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id="example-selenium-test-html"&gt;Example Selenium Test HTML&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;xmlns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/1999/xhtml"&lt;/span&gt; &lt;span class="na"&gt;xml:lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt; &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://selenium-ide.openqa.org/profiles/test-case"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/html; charset=UTF-8"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"selenium.base"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://mysite.com/"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;selenium-login&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt; &lt;span class="na"&gt;cellpadding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;cellspacing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;thead&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt; &lt;span class="na"&gt;rowspan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;colspan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;selenium-login&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;thead&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tbody&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;open&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;/&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;type&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;id=email&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;admin@mysite.com&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;type&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;id=password&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;examplepassword&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;clickAndWait&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;id=signin&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;assertLocation&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;https://mysite.com/home&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;assertElementPresent&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;link=Launch the web app&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tbody&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To get started with the IDE automation framework for interactions with websites (like a programmatic browser) &lt;a href="http://docs.seleniumhq.org/docs/02_selenium_ide.jsp#introduction"&gt;http://docs.seleniumhq.org/docs/02_selenium_ide.jsp#introduction&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="selenium-webdriver"&gt;Selenium WebDriver&lt;/h3&gt;
&lt;p&gt;Characteristics of good test plans are focusing on "happy path" high value tests, avoiding fragility/brittleness (any change invalidates many tests), and of course avoiding manual intervention.&lt;/p&gt;
&lt;p&gt;Having a huge suite of IDE based tests incurs potentially unsustainable technical testing debt &lt;em&gt;(e.g. investigating/rewriting why/when many tests fail).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And requiring a testing machine with a UI with a browser etc. is a lot of overhead if you intend on (correctly!) running the tests really often.&lt;/p&gt;
&lt;p&gt;Converting raw .html tests to a specific language binding (i.e. moving more to WhiteBox) can remove non essential parts of the test and increase flexibility (multiple browsers!).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo pip install selenium&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;installation assuming linux and python&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="example-selenium-python-webdriver"&gt;Example Selenium Python Webdriver&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;The IDE helpfully not only can save a Test but can File -&amp;gt; Export Test Case As -&amp;gt; python&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.webdriver.common.by&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;By&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.webdriver.common.keys&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Keys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.webdriver.support.ui&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Select&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.common.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NoSuchElementException&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SeleniumLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Firefox&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;implicitly_wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://mysite.com/"&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verificationErrors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept_next_alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_selenium_login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;
    &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"admin@mysite.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"examplepassword"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"signin"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://mysite.com/home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_element_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LINK_TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Launch the web app"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_element_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;how&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;how&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;NoSuchElementException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_alert_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;switch_to_alert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;NoAlertPresentException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close_alert_and_get_its_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;switch_to_alert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;alert_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept_next_alert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dismiss&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;alert_text&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept_next_alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tearDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verificationErrors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;python selenium-login.py runs the tests (opens Firefox, executes all of the commands)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Docs for the webdriver interface:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://selenium-python.readthedocs.io/api.html"&gt;https://selenium-python.readthedocs.io/api.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://seleniumhq.github.io/selenium/docs/api/py/api.html"&gt;https://seleniumhq.github.io/selenium/docs/api/py/api.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;useful for looking up things like &lt;code&gt;driver.current_url&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="selenium-performance-with-phantomjs"&gt;Selenium Performance with PhantomJS&lt;/h3&gt;
&lt;p&gt;Removing the UI requirement improves test performance and increases the options of where the test is run (Dev machines, headless VMs, cloud servers, etc.)&lt;/p&gt;
&lt;p&gt;Installing the python selenium library, downloading the phantomJS binary running it was relatively easy with Ubuntu Linux since the project integrated GhostDriver too.&lt;/p&gt;
&lt;p&gt;The phantomjs headless browser &lt;a href="http://phantomjs.org"&gt;http://phantomjs.org&lt;/a&gt; &lt;strong&gt;Archived as of 2018-03 and no longer under active development&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id="installing-phantomjs"&gt;Installing PhantomJS&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Archived as of 2018-03 and no longer under active development&lt;/strong&gt; &lt;a href="http://phantomjs.org/download.html"&gt;http://phantomjs.org/download.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For example for Linux one might use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;wget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;bitbucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ariya&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;phantomjs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;downloads&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;phantomjs&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.9&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;linux&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x86_64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bz2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;tar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xjvf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;phantomjs&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.9&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;linux&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x86_64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bz2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="example-phantomjs-webdriver-python-script"&gt;Example PhantomJS webdriver python script&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;mini script to just show usage&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;

&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PhantomJS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;executable_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'/opt/phantomjs-1.9.8-linux-x86_64/bin/phantomjs'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;9134&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_url&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"done"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;To run the phantomJS binary&lt;/strong&gt; &lt;code&gt;phantomjs-1.9.8-linux-x86_64/bin/phantomjs --webdriver=9134&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ghostdriver included and running on port 9134&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As an example I found PhantomJS to be at least twice as fast as Firefox
    ./phantomjs --webdriver=127.0.0.1:9134 --ignore-ssl-errors=true&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;skip verifying SSL &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="advanced-python-and-phantomjs-example"&gt;Advanced Python and PhantomJS example&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;more complete example with python unittest framework (used the Firefox Selenium IDE plugin -&amp;gt; Export)&lt;/p&gt;
&lt;p&gt;logs in, asserts there is an Admin tab which when clicked shows Group Info&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.webdriver.common.by&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;By&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.webdriver.common.keys&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Keys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.webdriver.support.ui&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Select&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.common.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NoSuchElementException&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SeleniumAdminLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PhantomJS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'/opt/phantomjs-1.9.2-linux-x86_64/bin/phantomjs'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;9134&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;implicitly_wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://myexample.org"&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verificationErrors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept_next_alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_selenium_admin_login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"email"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"email"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"admin@example.org"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"password"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"password"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"mypassword"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"signin"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://myexample.org/home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_element_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LINK_TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Launch the web app"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_element_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CSS_SELECTOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"a.admin &amp;gt; span"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_css_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"a.admin &amp;gt; span"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Group Info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_css_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"h1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_element_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;how&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;how&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;NoSuchElementException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_alert_present&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;switch_to_alert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;NoAlertPresentException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close_alert_and_get_its_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;switch_to_alert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;alert_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept_next_alert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dismiss&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;alert_text&lt;/span&gt;
            &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept_next_alert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tearDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verificationErrors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="basic-polling-with-firefox-and-mac"&gt;Basic Polling with Firefox and Mac&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;urllib2&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;selenium&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webdriver&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;selenium.webdriver.firefox.firefox_binary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FirefoxBinary&lt;/span&gt;


&lt;span class="n"&gt;FIREFOX_MAC_PATH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/Applications/Firefox.app/Contents/MacOS/firefox-bin'&lt;/span&gt;
&lt;span class="n"&gt;G_URL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'https://g.example.com'&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'__main__'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Usage error: requires a username'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# https://docs.python.org/2/library/datetime.html#strftime-strptime-behavior&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;new_server_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;%b%H%M%S'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FIREFOX_MAC_PATH&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FirefoxBinary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;firefox_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FIREFOX_MAC_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Firefox&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;G_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'login'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_css_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'input[type="submit"]'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;/deploy/simple_server'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;G_URL&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_server_name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'hostname'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'hostname'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_server_name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element_by_css_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'input[type="submit"]'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'https://&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;.example.com'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_server_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'requested build of &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getcode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'waiting for &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'done'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;More Info&lt;/strong&gt; &lt;a href="https://realpython.com/headless-selenium-testing-with-python-and-phantomjs/"&gt;https://realpython.com/headless-selenium-testing-with-python-and-phantomjs/&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="selenium"/><category term="python"/><category term="programming"/><category term="test"/><category term="phantomjs"/><category term="CI"/><category term="tech debt"/></entry><entry><title>Attack of the Spiders, Bots, and Crawlers</title><link href="https://blog.john-pfeiffer.com/attack-of-the-spiders-bots-and-crawlers/" rel="alternate"/><published>2013-07-20T23:00:00-07:00</published><updated>2013-07-20T23:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2013-07-20:/attack-of-the-spiders-bots-and-crawlers/</id><summary type="html">&lt;p&gt;It is well known that search engines need to index what the contents of webpages are in order to return accurate results to Users. It may not be well known how much traffic that generates.&lt;/p&gt;
&lt;p&gt;In this sampling from my logs (not representative of anything), 57% of the traffic in …&lt;/p&gt;</summary><content type="html">&lt;p&gt;It is well known that search engines need to index what the contents of webpages are in order to return accurate results to Users. It may not be well known how much traffic that generates.&lt;/p&gt;
&lt;p&gt;In this sampling from my logs (not representative of anything), 57% of the traffic in the log is from bots (I'm not turning them away, skip to the end on how to do that).&lt;/p&gt;
&lt;p&gt;Drupal (CMS) is remarkably good at making content for bots to read so it's not surprising they're all slurping, and of course if you want to be popular you need all of those indexes to know about you...&lt;/p&gt;
&lt;h3 id="how-many-hits-from-each-kind-of-bot"&gt;How many hits from each kind of bot&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;cat john-pfeiffer.access | wc -l&lt;/code&gt; &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1108 hits&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat john-pfeiffer.access | grep -v &amp;quot;Baiduspider&amp;quot; | grep -v &amp;quot;bingbot&amp;quot; | grep -v &amp;quot;YandexBot&amp;quot; | grep -v &amp;quot;Sogou&amp;quot; | grep -v &amp;quot;Mail.RU\_Bot&amp;quot; | grep -v &amp;quot;Googlebot&amp;quot; | grep -v &amp;quot;SISTRIX Crawler&amp;quot; | grep -v &amp;quot;MJ12bot&amp;quot; | wc -l  
637
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;cat john-pfeiffer.access | grep "Googlebot" | wc -l&lt;/code&gt; &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;105 hits from &lt;a href="https://en.wikipedia.org/wiki/Googlebot"&gt;https://en.wikipedia.org/wiki/Googlebot&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cat john-pfeiffer.access | grep "bingbot" | wc -l&lt;/code&gt; &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;96 hits from &lt;a href="http://en.wikipedia.org/wiki/Bingbot"&gt;http://en.wikipedia.org/wiki/Bingbot&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cat john-pfeiffer.access | grep "Baiduspider" | wc -l&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;93 hits from &lt;a href="http://baidu.com/search/spider_english.html"&gt;http://baidu.com/search/spider_english.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cat john-pfeiffer.access | grep "SISTRIX Crawler" | wc -l&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;76 hits from &lt;a href="http://crawler.sistrix.net"&gt;http://crawler.sistrix.net&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cat john-pfeiffer.access | grep "YandexBot" | wc -l&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;64 hits from &lt;a href="http://www.botopedia.org/user-agent-list/search-bots/yandex-bot"&gt;http://www.botopedia.org/user-agent-list/search-bots/yandex-bot&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cat john-pfeiffer.access | grep "Mail.RU\_Bot" | wc -l&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;23 hits from &lt;a href="http://www.webmasterworld.com/search_engine_spiders/4520951.htm"&gt;http://www.webmasterworld.com/search_engine_spiders/4520951.htm&lt;/a&gt; , &lt;a href="http://www.botopedia.org/user-agent-list/search-bots/mailru-bot"&gt;http://www.botopedia.org/user-agent-list/search-bots/mailru-bot&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cat john-pfeiffer.access | grep "MJ12bot" | wc -l&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;12 hits from &lt;a href="http://www.majestic12.co.uk/projects/dsearch/mj12bot.php"&gt;http://www.majestic12.co.uk/projects/dsearch/mj12bot.php&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cat john-pfeiffer.access | grep "Sogou" | wc -l&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;7 hits from &lt;a href="http://www.botopedia.org/user-agent-list/search-bots/sogou-spider"&gt;http://www.botopedia.org/user-agent-list/search-bots/sogou-spider&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="more-info-on-bots-and-crawlers"&gt;More Info on Bots and Crawlers&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://www.incapsula.com/the-incapsula-blog/item/393-know-your-top-10-bots"&gt;http://www.incapsula.com/the-incapsula-blog/item/393-know-your-top-10-bots&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://searchenginewatch.com/article/2067357/Bye-bye-Crawler-Blocking-the-Parasites"&gt;http://searchenginewatch.com/article/2067357/Bye-bye-Crawler-Blocking-the-Parasites&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(Besides robots.txt you are pretty much left with ban by User-Agent or IP Address Range.)&lt;/p&gt;</content><category term="linux"/></entry><entry><title>Git Basics and Hard to Scratch Itches</title><link href="https://blog.john-pfeiffer.com/git-basics-and-hard-to-scratch-itches/" rel="alternate"/><published>2013-07-07T12:34:00-07:00</published><updated>2013-07-07T12:34:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2013-07-07:/git-basics-and-hard-to-scratch-itches/</id><summary type="html">
&lt;p&gt;Like "tabs vs spaces" there are tools and choices which create more controversy than others.&lt;/p&gt;
&lt;p&gt;Here's my pragmatic take on how to get stuff done...&lt;/p&gt;
&lt;h3 id="git-cheat-sheet"&gt;Git Cheat Sheet&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git branch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;see the current branch&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;are there any files changed?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git stash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;save and hide any modified files to …&lt;/p&gt;&lt;/blockquote&gt;</summary><content type="html">
&lt;p&gt;Like "tabs vs spaces" there are tools and choices which create more controversy than others.&lt;/p&gt;
&lt;p&gt;Here's my pragmatic take on how to get stuff done...&lt;/p&gt;
&lt;h3 id="git-cheat-sheet"&gt;Git Cheat Sheet&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git branch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;see the current branch&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;are there any files changed?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git stash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;save and hide any modified files to a temporary cache&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git checkout master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;set the local working files to the shared canonical branch (aka "master")&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git pull
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;get and merge any changes, many others prefer the more precise git fetch&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;examine the history of changes made upstream (consider pre-emptively how the merge will affect things)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git checkout mybranch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;change the focus back to the working dev branch&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git pull
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;ensure the local working files from the branch are the latest (i.e. if other contributors or devices have made modifications)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git merge master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;force reconciliation of master and the current working branch&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;RESOLVE CONFLICTS IN THE CODE MANUALLY&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;one thing to remember is that "merge" is best effort so sometimes code/lines will be added or removed that are not the desired semantic outcome&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git commit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;indicate a merge from master and any conflict resolution fixes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;never force&lt;/strong&gt; , the changed branch may be worked on by others and a forked history means no reconciliation...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="extra-credit"&gt;Extra Credit&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cp -a FOOBAR /tmp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;manually back up the files&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git checkout -- ./name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;reset a single changed file back to the previously committed version&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git log ; git revert COMMITID
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;identify a mistaken commit and create a commit to undo all changes until the working files (and git history) match that specific commit id&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git reset --soft HEAD^^
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;correct and recommit change A, then go back to recommitting change B&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git reset HARD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;reset all of the working files to a previously committed version&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;rm -rf FOOBAR; git clone
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;throw away entirely a locally copy and start again from the remote version&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="simple-git-theory"&gt;Simple Git Theory&lt;/h3&gt;
&lt;p&gt;Distributed Version Control&lt;/p&gt;
&lt;p&gt;Directed Acyclical Graph&lt;/p&gt;
&lt;p&gt;Any branch can be "master".&lt;/p&gt;
&lt;p&gt;But in practice most developes use an agreed centralized location (i.e. good availability/uptime), especially a SaaS like github or bitbucket.&lt;/p&gt;
&lt;h3 id="basic-git-commands"&gt;Basic Git Commands&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git clone&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;SSH is far more convenient and secure&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;git clone&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;HTTPS is common for "read-only" downloads of an open source project&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;git add --all .&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the extra parameter ensures new files are added (which I otherwise forget), and as a bonus will detect file/directory renaming&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;git log&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Show the history of commits (and hashes)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;git rebase -i&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;interactively rewrite git history, NEVER do this on a shared/collaborative branch/repository&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="remove-a-large-file-from-history"&gt;Remove a large file from history&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git filter-branch --tree-filter "rm -rf VeryLargeDirOrFileName" -f HEAD --all&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://git-scm.com/docs/git-filter-branch"&gt;https://git-scm.com/docs/git-filter-branch&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10 | awk '{print$1}')"&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="split-a-subdirectory-into-its-own-repo"&gt;Split a subdirectory into its own repo&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git subtree push --prefix examplesubdir https://bitbucket.org/USERNAME/examplereponame master&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;whoa, one line to take a subdirectory and push it into the waiting empty new repo&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://developer.atlassian.com/blog/2015/05/the-power-of-git-subtree/"&gt;https://developer.atlassian.com/blog/2015/05/the-power-of-git-subtree/&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="git"/></entry><entry><title>Logic Puzzles</title><link href="https://blog.john-pfeiffer.com/logic-puzzles/" rel="alternate"/><published>2013-05-13T05:31:00-07:00</published><updated>2013-05-13T05:31:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2013-05-13:/logic-puzzles/</id><summary type="html">
&lt;h4 id="how-to-have-two-1-hour-uneven-burning-fuses-burn-for-45-mins"&gt;How to have two 1 hour uneven burning fuses burn for 45 mins?&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;light only ONE end &lt;/li&gt;
&lt;li&gt;burn both ends = 30 mins&lt;/li&gt;
&lt;li&gt;when it finishes, light the other end&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="three-light-bulbs-are-all-off-in-one-room-three-light-switches-in-another"&gt;Three light bulbs are all off in one room, three light switches in another...&lt;/h4&gt;
&lt;p&gt;...If you can't see the bulbs from …&lt;/p&gt;</summary><content type="html">
&lt;h4 id="how-to-have-two-1-hour-uneven-burning-fuses-burn-for-45-mins"&gt;How to have two 1 hour uneven burning fuses burn for 45 mins?&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;light only ONE end &lt;/li&gt;
&lt;li&gt;burn both ends = 30 mins&lt;/li&gt;
&lt;li&gt;when it finishes, light the other end&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="three-light-bulbs-are-all-off-in-one-room-three-light-switches-in-another"&gt;Three light bulbs are all off in one room, three light switches in another...&lt;/h4&gt;
&lt;p&gt;...If you can't see the bulbs from the switch room, how can you tell which connects to which if you can only enter the bulb room once?&lt;/p&gt;
&lt;p&gt;Flip the first switch for one bulb for many minutes, then turn it off.&lt;/p&gt;
&lt;p&gt;Flip the second switch on. Enter the bulb room, the bulb on connects to the second switch, the warmer of the two bulbs off is the first switch.  &lt;/p&gt;
&lt;h4 id="at-night-4-people-arrive-at-one-side-of-a-bridge"&gt;At night 4 people arrive at one side of a bridge...&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;...that can only support 2 people at a time and they have only one flashlight.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If each were alone here's how fast they could cross: the first in 15 minutes, the second in 10 minutes, the third in 2 minutes and the fourth in 1 minute. &lt;/p&gt;
&lt;p&gt;If the flashlight only has 17 minutes how do they cross?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The 1 minute and 2 minute cross together (takes 2 minutes). &lt;/li&gt;
&lt;li&gt;The 1 minute returns (total is now 3 minutes). &lt;/li&gt;
&lt;li&gt;The 10 and 5 minutes cross together (total is now 13 minutes). &lt;/li&gt;
&lt;li&gt;The 2 minute returns (the total is now 15 minutes). &lt;/li&gt;
&lt;li&gt;Finally the 1 minute and 2 minute cross again (17 minutes and complete).  &lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="3-bags-contain-all-oranges-all-apples-or-a-mix-of-oranges-and-apples"&gt;3 bags contain all oranges, all apples, or a mix of oranges and apples...&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;...BUT the label on each definitely is incorrect. How do you find out which bag has what and how many bags to you need to open?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Choose the one labeled "mixed" and if it contains apples, therefore the remaining bag which is labelled oranges must not contain all oranges and so must be the mixed one.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id="there-are-3-red-hats-and-2-blue-hats-in-a-bag"&gt;There are 3 red hats and 2 blue hats in a bag....&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Andrew, Bob, and Carol each reach into the box and place one of the hats on their own head.
They cannot see what color hat they have chosen.
Then they sit down all facing in the same direction: Andrew &amp;lt;-- Bob &amp;lt;--Carol&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Such that Carol can see what color Bob and Andrew are wearing. &lt;/li&gt;
&lt;li&gt;Bob can see what color Andrew is wearing. &lt;/li&gt;
&lt;li&gt;Andrew can't see anyone's hats.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The first person to say what color hat they are wearing wins.&lt;/strong&gt; 
The winner of the game gets a billion dollars... But if they guess wrong, they die... so nobody wants to try "just guessing"...&lt;/p&gt;
&lt;p&gt;SO... after they grab their hats and sit down, a long time passes...&lt;/p&gt;
&lt;p&gt;Eventually somebody says "I have the answer" and successfully states what color hat they have.  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Since Carol can see Andrew and Bob, if they both were wearing blue hats she would immediately know that she was wearing red, so unlikely that a long time would pass before she answered (and take the money)...&lt;/li&gt;
&lt;li&gt;Therefore Andrew and Bob have one red hat and one blue hat. (And Carol knows there's a 33% chance she's got a blue hat and a 66% chance she's got a red hat).&lt;/li&gt;
&lt;li&gt;Since Bob knows that Carol delayed in answering "a long time passes" AND he can see the color of hat that Andrew is wearing, then&lt;/li&gt;
&lt;li&gt;Either Andrew is wearing a blue hat, and therefore Bob must be wearing a red hat.  &lt;/li&gt;
&lt;li&gt;Or Andrew is wearing a red hat, and therefore Bob must be wearing a blue hat.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Bob successfully answers...&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(unless Carol is more interested in sadistic plots and even though she knows both Bob and Andrew have blue hats she just waits to watch Bob get it wrong...)&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;NOTE: this variation from the NY Times is slightly incorrect as it is essentially asking why Andrew wins... 
The clue in order for "Andrew" to know (the delay) is the same as what Bob uses, but Bob will be able to deduce it much faster. 
(Again, Carol could be deliberately waiting to watch Andrew fail too...)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://tierneylab.blogs.nytimes.com/2009/03/16/the-puzzle-of-the-3-hats"&gt;http://tierneylab.blogs.nytimes.com/2009/03/16/the-puzzle-of-the-3-hats&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="one-ball-of-8-weighs-more-than-the-other-7-how-can-you-identify-it-with-two-measurings-of-a-balance-scale"&gt;One ball of 8 weighs more than the other 7, how can you identify it with two measurings of a balance scale?&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;&lt;br/&gt;
Weigh 3 vs 3, if they match then weigh one of the "good" ones against
one of the remaining ones, if they're unequal you have the heavier one,
if they're equal then the last is the heavy one.  &lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If the 3 vs 3 do not match then take two of the heavy side and weigh
them against each other: if they're unequal you have the heavier one, if
they're equal then the last of the heavy side is the heavy one.&lt;br/&gt;
&lt;/p&gt;
&lt;h4 id="one-ball-of-12-weighs-more-or-less-than-the-other-11-how-can-you-identify-it-and-whether-its-heavier-or-lighter-with-three-measurings-of-a-balance-scale"&gt;One ball of 12 weighs more OR less than the other 11, how can you identify it and whether it's heavier or lighter with three measurings of a balance scale?&lt;/h4&gt;
&lt;p&gt;&lt;br/&gt;
&lt;em&gt;Weigh 4 vs 4, label them ABCD and EFGH. If they balance then weigh
"good" ABC against WXY. If the second weighing balances then weigh Z
against A to see if Z is light or heavy. If the second weighing has WXY
heavier or lighter (make a note of which as we have solved whether the
imbalance is heavy or light), then weigh W with X. If they balance then
Y is the imbalance from step 2 (heavier or lighter). If they do not
balance then whichever matches the imbalance from step 2 is the off
ball.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;IF at the first weighing ABCD was lighter than EFGH, in the second
weighing rotate such that ABCZ is weighed against D with "good" XYZ. If
...&lt;/p&gt;

&lt;p&gt;Note that this problem can also be solved by carefully measuring the
results as the left and right groups of 4 are rotated.&lt;/p&gt;

&lt;p&gt;12 becomes 4 v 4 v (4) - if the balance the last 4 are easy  &lt;/p&gt;
&lt;p&gt;if not balanced: 1,2,5 vs 3,6,12&lt;/p&gt;

&lt;h4 id="5-pots-of-10g-coins-but-one-contains-9g-coins-which-pot-is-off-by-measuring-the-weight-once"&gt;5 pots of 10g coins BUT one contains 9g coins, which pot is off by measuring the weight once?&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;&lt;br/&gt;
1 from pot 1, 2 from pot 2, 3 from pot 3, 4 from pot 4, and 5 from pot 5  &lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Expected weight should be 150g: if it’s 149 then pot 1 , if it’s 148 then pot 2, etc.&lt;br/&gt;
&lt;/p&gt;
&lt;h4 id="how-can-you-use-a-weighted-coin-ie-head-more-than-tails-and-still-create-a-fair-system-of-flips"&gt;How can you use a weighted coin (i.e. head more than tails) and still create a fair system of flips?&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;Aggregate the outcomes: treat two flips as a single result such that heads then tails = Heads, and tails followed by heads = Tails.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id="10-are-heads-of-n-coins-how-to-create-2-groups-that-have-the-same-number-of-heads-up"&gt;10 are heads of N coins, how to create 2 groups that have the same number of heads up?&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;Take any 10 from N and flip them (inverted will mirror the number of heads in the N - 10 group)&lt;/em&gt;&lt;/p&gt;
&lt;h4 id="3-colors-of-socks-how-many-to-take-out-until-you-have-a-pair"&gt;3 colors of socks: how many to take out until you have a pair?&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;4 (1 of each and the last 1 must match 1 of the first 3)&lt;/em&gt;&lt;/p&gt;
&lt;h4 id="100-closed-lockers-open-all-of-the-lockers-on-the-first-pass"&gt;100 closed lockers, open all of the lockers on the first pass...&lt;/h4&gt;
&lt;p&gt;** ...close every 2nd locker on the second pass, on the third pass and for every 3rd locker open it if it's closed, close it if it's open.&lt;strong&gt;
&lt;/strong&gt;After the hundreth pass, how many lockers are open?**&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;br/&gt;
i.e. after the hundreth the locker 12 was opened on pass 1, closed pass 2, opened pass 3, closed pass 4, opened pass 6, closed pass 12.  &lt;/em&gt;&lt;/p&gt;
&lt;p&gt;locker 13 (prime) was opened on pass 1 and closed on pass 13.&lt;/p&gt;
&lt;p&gt;locker 16: opened on 1, closed on 2, opened on 4, closed on 8, opened on 16!&lt;/p&gt;

&lt;p&gt;There are 9 perfect squares under one hundred: 1, 4, 9, 16, 25, 36, 49, 64, 81  &lt;/p&gt;

&lt;h4 id="incomplete-puzzles"&gt;Incomplete Puzzles&lt;/h4&gt;

&lt;p&gt;Minutes Degrees from 12:00 = mins * 6  &lt;/p&gt;
&lt;p&gt;Hours Degrees From 12:00 = hours * 30 + mins * .5&lt;/p&gt;

&lt;p&gt;Find 1 duplicate in 100 numbers of 1 to 100 = add them up and subtract sum of 1 to 100  &lt;/p&gt;
&lt;p&gt;ALTERNATIVES: hashmap O(1), sort and then scan looking for a double entry (nLog(n) if mergesort? + n ), n\^2 if using brute force&lt;/p&gt;

&lt;p&gt;Sorted List rotated: find min = naive if curr &amp;lt; prev is O(n)  &lt;/p&gt;
&lt;p&gt;Binary Search compare first and mid, if ordered then reset is in partition &amp;gt; mid (RIGHT)  &lt;/p&gt;
&lt;p&gt;find any elem: if &amp;gt; first and Ordered then LEFT&lt;/p&gt;

&lt;p&gt;BAD: binary search against the "fixed" list using the min location as an offset with mod list size ?&lt;/p&gt;

&lt;p&gt;nxn matrix of numbers in ascending order in both dimensions how would you go about finding if the number y is in the matrix.&lt;/p&gt;

&lt;p&gt;5623 players in a tournament, how many matches must be played?&lt;/p&gt;
&lt;p&gt;5622 (everybody loses at least once except the winner)&lt;/p&gt;

&lt;h4 id="unsolved-puzzles"&gt;Unsolved Puzzles&lt;/h4&gt;

&lt;p&gt;N different flavored Cakes (each with a different Volume) for K people,
each person should get an equal volume of Cake (but only a single
flavor)  &lt;/p&gt;
&lt;p&gt;V’s 1, 2, 3, 4 for 5 people  &lt;/p&gt;
&lt;p&gt;add all volumes together / people = “theoretical best” (i.e. 10/5 = 2)  &lt;/p&gt;
&lt;p&gt;start with 2 cakes, 4 + 1, so V=1 for K, next 4 + 2 = doesn’t work, 4+3
doesn’t work  &lt;/p&gt;
&lt;p&gt;3 cakes: 4+3+2 = 9 BUT not divisible by 5  &lt;/p&gt;
&lt;p&gt;1 * 1, 2 * 1, 2 * 1 = waste 5 ( 10-5)  &lt;/p&gt;
&lt;p&gt;discard&lt;/p&gt;

&lt;h4 id="random-facts"&gt;Random Facts&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;World Population (estimated 2012) = 7 Billion  &lt;/li&gt;
&lt;li&gt;US Population (estimated 2012) = 316 Million  &lt;/li&gt;
&lt;li&gt;Earth circumference (equator) = 24,901.55 miles (40,075.16 kilometers)&lt;/li&gt;
&lt;/ul&gt;</content><category term="puzzles"/></entry><entry><title>Best Computer Science online and a More Complete Education</title><link href="https://blog.john-pfeiffer.com/best-computer-science-online-and-a-more-complete-education/" rel="alternate"/><published>2013-05-09T16:52:00-07:00</published><updated>2013-05-09T16:52:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2013-05-09:/best-computer-science-online-and-a-more-complete-education/</id><summary type="html">
&lt;p&gt;Learning is a life long pleasure.  Programming and Computer Science are challenging and take time but luckily the resources available today make it free to get access to top quality materials.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;In the 6 years since I first wrote this article many of the links had to be updated (and …&lt;/em&gt;&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Learning is a life long pleasure.  Programming and Computer Science are challenging and take time but luckily the resources available today make it free to get access to top quality materials.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;In the 6 years since I first wrote this article many of the links had to be updated (and some resources are no longer free, goodbye MOOC), hopefully I have managed to maintain this somewhat&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="just-starting-with-coding-mostly-python"&gt;Just starting with coding (mostly python)&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;I've been asked often enough about getting started in software programming that I decided prepend this "guide" from 2019 that I believe is a free and easy way to get bootstrapped.&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Watch a couple of videos from &lt;a href="https://www.coursera.org/learn/interactive-python-1"&gt;https://www.coursera.org/learn/interactive-python-1&lt;/a&gt; &lt;em&gt;(pro tip, if possible watch at 1.25x or 1.5x speed and pause and go back to replay a specific part when necessary)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Do about 3 hackerrank exercises:
e.g.- &lt;a href="https://www.hackerrank.com/domains/python?filters%5Bsubdomains%5D%5B%5D=py-introduction"&gt;https://www.hackerrank.com/domains/python?filters%5Bsubdomains%5D%5B%5D=py-introduction&lt;/a&gt; (but stop after doing "Errors/Exceptions" and move onto the problem-solving track &lt;a href="https://www.hackerrank.com/domains/algorithms?badge_type=problem-solving"&gt;https://www.hackerrank.com/domains/algorithms?badge_type=problem-solving&lt;/a&gt; , always feel free to skip an exercise if the problem description is too confusing, the key is practice not perfection ;)
, &lt;a href="https://www.hackerrank.com/challenges/python-string-split-and-join/problem"&gt;https://www.hackerrank.com/challenges/python-string-split-and-join/problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Do one "chapter" from KhanAcademy (e.g. &lt;strong&gt;&lt;a href="https://www.khanacademy.org/computing/computer-science/algorithms"&gt;https://www.khanacademy.org/computing/computer-science/algorithms&lt;/a&gt;&lt;/strong&gt; start from Binary search and you're "good enough" after finishing merge sort)&lt;/li&gt;
&lt;li&gt;Read one "chapter" from the PythonDocs , e.g. start with &lt;a href="https://docs.python.org/3.7/tutorial/introduction.html"&gt;https://docs.python.org/3.7/tutorial/introduction.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Watch one video from MIT &lt;a href="https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in-python-fall-2016/lecture-videos/"&gt;https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in-python-fall-2016/lecture-videos/&lt;/a&gt;
note this is the same as &lt;a href="https://www.edx.org/course/introduction-to-computer-science-and-programming-using-python-3"&gt;https://www.edx.org/course/introduction-to-computer-science-and-programming-using-python-3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Do one exercise from leetcode , &lt;a href="https://leetcode.com/problemset/algorithms/?difficulty=Easy&amp;amp;listId=79h8rn6"&gt;https://leetcode.com/problemset/algorithms/?difficulty=Easy&amp;amp;listId=79h8rn6&lt;/a&gt; &lt;em&gt;(easy ones first, then go to mediums or the top100 list)&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;e.g.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(if statements for each edge case) &lt;a href="https://leetcode.com/problems/valid-parentheses"&gt;https://leetcode.com/problems/valid-parentheses&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;(nested for loops and dicts/hashmaps) &lt;a href="https://leetcode.com/problems/two-sum"&gt;https://leetcode.com/problems/two-sum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;(nested for loops) &lt;a href="https://leetcode.com/problems/valid-sudoku"&gt;https://leetcode.com/problems/valid-sudoku&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then go back to #1 and repeat =]&lt;/p&gt;
&lt;p&gt;Of course what I've suggested is really just the minimum.  Some good auxiliary learning would be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://exercism.io/tracks/python"&gt;https://exercism.io/tracks/python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.khanacademy.org/computing/computer-science/internet-intro"&gt;https://www.khanacademy.org/computing/computer-science/internet-intro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://scotch.io/bar-talk/a-quick-understanding-of-rest"&gt;https://scotch.io/bar-talk/a-quick-understanding-of-rest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask"&gt;https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://programminghistorian.org/en/lessons/creating-apis-with-python-and-flask"&gt;https://programminghistorian.org/en/lessons/creating-apis-with-python-and-flask&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="introduction-to-programming-by-universities"&gt;Introduction to Programming by Universities&lt;/h2&gt;
&lt;h3 id="stanford"&gt;Stanford&lt;/h3&gt;
&lt;p&gt;by Mehran Sahami (very fun and Java is an ok starting point - though the world has moved to Go)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://see.stanford.edu/Course/CS106A"&gt;https://see.stanford.edu/Course/CS106A&lt;/a&gt; Programming Methodology&lt;/li&gt;
&lt;li&gt;Then algorithms with &lt;a href="https://see.stanford.edu/Course/CS106B"&gt;https://see.stanford.edu/Course/CS106B&lt;/a&gt; Julie Zelenski&lt;/li&gt;
&lt;li&gt;And more algorithms &lt;a href="https://www.coursera.org/learn/algorithms-divide-conquer"&gt;https://www.coursera.org/learn/algorithms-divide-conquer&lt;/a&gt; Tim Roughgarden&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;And advanced algorithms &lt;a href="https://www.coursera.org/learn/algorithms-graphs-data-structures"&gt;https://www.coursera.org/learn/algorithms-graphs-data-structures&lt;/a&gt; Tim Roughgarden&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://see.stanford.edu/Course"&gt;https://see.stanford.edu/Course&lt;/a&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://online.stanford.edu/course/intro-computer-networking-winter-2014"&gt;https://online.stanford.edu/course/intro-computer-networking-winter-2014&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/playlist?list=PLvFG2xYBrYAQCyz4Wx3NPoYJOFjvU7g2Z"&gt;https://www.youtube.com/playlist?list=PLvFG2xYBrYAQCyz4Wx3NPoYJOFjvU7g2Z&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="harvard"&gt;Harvard&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cs50.harvard.edu/college/"&gt;https://cs50.harvard.edu/college/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=y62zj9ozPOM&amp;amp;list=PLvFG2xYBrYATXXCZdUUdh13gG4MEDqm2S"&gt;https://www.youtube.com/watch?v=y62zj9ozPOM&amp;amp;list=PLvFG2xYBrYATXXCZdUUdh13gG4MEDqm2S&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.extension.harvard.edu/open-learning-initiative/intensive-introduction-computer-science"&gt;https://www.extension.harvard.edu/open-learning-initiative/intensive-introduction-computer-science&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mit"&gt;MIT&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.edx.org/course/introduction-computer-science-python-mitx-6-00-1x"&gt;https://www.edx.org/course/introduction-computer-science-python-mitx-6-00-1x&lt;/a&gt; &lt;em&gt;now named&lt;/em&gt; &lt;a href="https://www.edx.org/course/6-00-1x-introduction-to-computer-science-and-programming-using-python-3"&gt;https://www.edx.org/course/6-00-1x-introduction-to-computer-science-and-programming-using-python-3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Algorithms of course!&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011"&gt;https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;(also at &lt;a href="https://itunes.apple.com/us/itunes-u/introduction-to-algorithms/id341597754"&gt;https://itunes.apple.com/us/itunes-u/introduction-to-algorithms/id341597754&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-046j-introduction-to-algorithms-sma-5503-fall-2005/video-lectures"&gt;https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-046j-introduction-to-algorithms-sma-5503-fall-2005/video-lectures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-046j-design-and-analysis-of-algorithms-spring-2015/lecture-videos/"&gt;https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-046j-design-and-analysis-of-algorithms-spring-2015/lecture-videos/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/"&gt;https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/&lt;/a&gt; (listing of all the MIT courses)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="princeton"&gt;Princeton&lt;/h3&gt;
&lt;p&gt;Algorithms
- &lt;a href="https://www.coursera.org/learn/algorithms-part1"&gt;https://www.coursera.org/learn/algorithms-part1&lt;/a&gt; (aka &lt;a href="https://online.princeton.edu/node/201"&gt;https://online.princeton.edu/node/201&lt;/a&gt;)
- &lt;a href="https://www.coursera.org/learn/algorithms-part2"&gt;https://www.coursera.org/learn/algorithms-part2&lt;/a&gt;
- &lt;a href="https://www.coursera.org/learn/cs-algorithms-theory-machines"&gt;https://www.coursera.org/learn/cs-algorithms-theory-machines&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="more-courses"&gt;More Courses&lt;/h2&gt;
&lt;h3 id="udacity-design-of-computer-programs"&gt;Udacity Design of Computer Programs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.udacity.com/course/cs101"&gt;https://www.udacity.com/course/cs101&lt;/a&gt; became &lt;a href="https://www.udacity.com/course/introduction-to-python--ud1110"&gt;https://www.udacity.com/course/introduction-to-python--ud1110&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.udacity.com/course/ud036"&gt;https://www.udacity.com/course/ud036&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.udacity.com/course/cs212"&gt;https://www.udacity.com/course/cs212&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.udacity.com/course/cs253"&gt;https://www.udacity.com/course/cs253&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.udacity.com/course/cs313"&gt;https://www.udacity.com/course/cs313&lt;/a&gt; &lt;em&gt;Intro to Theoretical Computer Science&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="udemy"&gt;Udemy&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.udemy.com/learn-how-to-code/#about-course"&gt;https://www.udemy.com/learn-how-to-code/#about-course&lt;/a&gt; (paid)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="information-and-models"&gt;Information and Models&lt;/h2&gt;
&lt;h3 id="mit_1"&gt;MIT&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-050j-information-and-entropy-spring-2008/"&gt;http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-050j-information-and-entropy-spring-2008/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;(also at &lt;a href="https://itunes.apple.com/us/itunes-u/information-and-entropy/id424082281"&gt;https://itunes.apple.com/us/itunes-u/information-and-entropy/id424082281&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="university-of-michigan"&gt;University of Michigan&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://class.coursera.org/modelthinking-2012-002/class/index"&gt;https://class.coursera.org/modelthinking-2012-002/class/index&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="software-engineering"&gt;Software Engineering&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.coursera.org/course/security"&gt;https://www.coursera.org/course/security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.udacity.com/course/ud805"&gt;https://www.udacity.com/course/ud805&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.udacity.com/course/cs258"&gt;https://www.udacity.com/course/cs258&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="vanderbilt"&gt;Vanderbilt&lt;/h3&gt;
&lt;p&gt;Software patterns
- &lt;a href="https://www.dre.vanderbilt.edu/~schmidt/Coursera/spring-2013-posa.html"&gt;https://www.dre.vanderbilt.edu/~schmidt/Coursera/spring-2013-posa.html&lt;/a&gt;
- &lt;a href="https://www.youtube.com/playlist?list=PLZ9NgFYEMxp6CHE-QQ040tlDILNcBqJnc"&gt;https://www.youtube.com/playlist?list=PLZ9NgFYEMxp6CHE-QQ040tlDILNcBqJnc&lt;/a&gt;
- &lt;a href="https://www.youtube.com/watch?v=2GZttnHChHo&amp;amp;list=PLZ9NgFYEMxp4ZsvD10uXmClGnukcu3Uff&amp;amp;index=13"&gt;https://www.youtube.com/watch?v=2GZttnHChHo&amp;amp;list=PLZ9NgFYEMxp4ZsvD10uXmClGnukcu3Uff&amp;amp;index=13&lt;/a&gt; (patterns)&lt;/p&gt;
&lt;h2 id="online-masters-in-cs"&gt;Online Masters in CS&lt;/h2&gt;
&lt;p&gt;There are even now recognized, accredited Masters degrees in Computer Science: &lt;a href="https://www.omscs.gatech.edu/"&gt;https://www.omscs.gatech.edu/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="practice-practice-practice"&gt;PRACTICE, PRACTICE, PRACTICE&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codingbat.com/python"&gt;https://codingbat.com/python&lt;/a&gt; (beginner)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.hackerrank.com"&gt;https://www.hackerrank.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leetcode.com"&gt;https://leetcode.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://projecteuler.net"&gt;https://projecteuler.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://acm.timus.ru"&gt;http://acm.timus.ru&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.spoj.com/problems/classical"&gt;http://www.spoj.com/problems/classical&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codeforces.com/problemset"&gt;https://codeforces.com/problemset&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tour.golang.org"&gt;https://tour.golang.org&lt;/a&gt; Go for a static language to complement Python&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.geeksforgeeks.org/javascript-tutorial/#basics"&gt;https://www.geeksforgeeks.org/javascript-tutorial/#basics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="interview-resources"&gt;Interview Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jwasham/coding-interview-university"&gt;https://github.com/jwasham/coding-interview-university&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://triplebyte.com/blog/how-to-pass-a-programming-interview"&gt;https://triplebyte.com/blog/how-to-pass-a-programming-interview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="things-often-not-covered-by-universities"&gt;Things Often Not Covered By Universities&lt;/h2&gt;
&lt;p&gt;For some reasons the "ivory tower" does not include all of the nitty gritty practicalities required to actually ship and run software in the real world.&lt;/p&gt;
&lt;p&gt;Here are some of those topics I wish were covered in the first year.  (Hint: they also help immensely in being gainfully employed)&lt;/p&gt;
&lt;h3 id="version-control"&gt;Version Control&lt;/h3&gt;
&lt;p&gt;The fundamental tool of managing change which was strangely ignored for a very long time in the short history of programming&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Version_control"&gt;https://en.wikipedia.org/wiki/Version_control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.atlassian.com/git/tutorials/what-is-version-control"&gt;https://www.atlassian.com/git/tutorials/what-is-version-control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://betterexplained.com/articles/a-visual-guide-to-version-control"&gt;https://betterexplained.com/articles/a-visual-guide-to-version-control&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="quality-and-testing"&gt;Quality and Testing&lt;/h3&gt;
&lt;p&gt;The practical answer to actually attempting to validate correctness in practice (rather than just logical proofs)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Software_testing"&gt;https://en.wikipedia.org/wiki/Software_testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://martinfowler.com/bliki/TestPyramid.html"&gt;http://martinfowler.com/bliki/TestPyramid.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html"&gt;https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.se-radio.net/2015/04/episode-224-sven-johann-and-eberhard-wolff-on-technical-debt"&gt;http://www.se-radio.net/2015/04/episode-224-sven-johann-and-eberhard-wolff-on-technical-debt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="build-and-continuous-integration"&gt;Build and Continuous Integration&lt;/h3&gt;
&lt;p&gt;Automation as a solution to the shortage of developer time and the exponential increase in software and complexity&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Build_automation"&gt;https://en.wikipedia.org/wiki/Build_automation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.drdobbs.com/tools/a-build-system-for-complex-projects-part/218400678"&gt;http://www.drdobbs.com/tools/a-build-system-for-complex-projects-part/218400678&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Continuous_integration"&gt;https://en.wikipedia.org/wiki/Continuous_integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.thoughtworks.com/continuous-integration"&gt;https://www.thoughtworks.com/continuous-integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.se-radio.net/2009/04/episode-133-continuous-integration-with-chris-read/"&gt;http://www.se-radio.net/2009/04/episode-133-continuous-integration-with-chris-read/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.ycombinator.com/item?id=15565875"&gt;https://news.ycombinator.com/item?id=15565875&lt;/a&gt; &lt;em&gt;(Write tests. Not too many. Mostly integration)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="performance-and-devops-and-operations"&gt;Performance and DevOps and Operations&lt;/h3&gt;
&lt;p&gt;With hardware having kept up with Moore's Law and "the cloud" providing so much elastic compute, performance is now often an afterthought. Additionally, how to quickly and efficiently deliver software has coalesced into the term DevOps.&lt;/p&gt;
&lt;p&gt;The big idea being that software that is not actually running is not very valuable ;)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Software_performance_testing"&gt;https://en.wikipedia.org/wiki/Software_performance_testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://jmeter.apache.org/"&gt;http://jmeter.apache.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://www.opensourcetesting.org/category/performance/"&gt;http://www.opensourcetesting.org/category/performance/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/DevOps"&gt;https://en.wikipedia.org/wiki/DevOps&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.atlassian.com/devops"&gt;https://www.atlassian.com/devops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/devops/what-is-devops/"&gt;https://aws.amazon.com/devops/what-is-devops/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.se-radio.net/2015/02/episode-221-jez-humble-on-continuous-delivery/"&gt;http://www.se-radio.net/2015/02/episode-221-jez-humble-on-continuous-delivery/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I know it sounds crazy that Computer Science practitioners (or Developers) should sully their hands with "Operations" but to truly understand the problem domains watching the logs or responding to an outage can vastly change how we write code.&lt;/p&gt;
&lt;p&gt;Importantly, seeing latency, traffic volume, and environmental issues makes us thankful when we do get back to the keyboard and can just focus on the theoretical aspects of a problem.&lt;/p&gt;</content><category term="programming"/><category term="computer science"/><category term="cs"/><category term="mooc"/><category term="free education"/><category term="distance learning"/><category term="cs on iphone"/><category term="programming videos"/><category term="programming"/><category term="online learning"/><category term="learning to code"/><category term="software"/><category term="beginner"/></entry><entry><title>Jinja2, a web html template layout for everyone</title><link href="https://blog.john-pfeiffer.com/jinja2-a-web-html-template-layout-for-everyone/" rel="alternate"/><published>2013-04-30T04:35:00-07:00</published><updated>2013-04-30T04:35:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2013-04-30:/jinja2-a-web-html-template-layout-for-everyone/</id><summary type="html">
&lt;p&gt;Web development used to be so hard (and static). (read &amp;lt;bold&amp;gt;)&lt;/p&gt;
&lt;p&gt;Now everyone realizes (along with version control and automated testing) that decoupling views and displays from dynamic code makes everyone's life easier! &lt;/p&gt;
&lt;p&gt;(Refresh your colors and layout without having to touch your backend logic! Re-engineer your persistence layer and …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Web development used to be so hard (and static). (read &amp;lt;bold&amp;gt;)&lt;/p&gt;
&lt;p&gt;Now everyone realizes (along with version control and automated testing) that decoupling views and displays from dynamic code makes everyone's life easier! &lt;/p&gt;
&lt;p&gt;(Refresh your colors and layout without having to touch your backend logic! Re-engineer your persistence layer and business logic but leave your uber wow design intact! Allow front end and back end developers to work in parallel!)&lt;/p&gt;
&lt;p&gt;Jinja2 is an excellent framework for python developers (to go with your uwsgi + django or webapp2) to get html templates (do not repeat yourself!) that can show off all of your backend python data magic with some pizazz.&lt;/p&gt;
&lt;p&gt;It's fairly easy to do all sorts of powerful things (like accessing variables, loops, etc.)
&lt;a href="http://jinja.pocoo.org/docs"&gt;http://jinja.pocoo.org/docs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When combined with css and jquery (i.e. tablesorter) you can quickly throw together a decent looking interactive experience.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://jquery.com/download"&gt;http://jquery.com/download&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tablesorter.com/docs"&gt;http://tablesorter.com/docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's my budding ode to sorting algorithms (yes, Google App Engine is free)
&lt;a href="http://john-pfeiffer.appspot.com/algorithms"&gt;http://john-pfeiffer.appspot.com/algorithms&lt;/a&gt;  #TODO: finish using jquery tablesorter&lt;/p&gt;
&lt;p&gt;A webapp2 project layout and source code example (please excuse the code formatting, you'll need to imagine the correct indents)...&lt;/p&gt;
&lt;h2 id="file-layout"&gt;File Layout&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;css&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;style&lt;/span&gt;.&lt;span class="nv"&gt;css&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nv"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;css&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;tablesorter&lt;/span&gt;.&lt;span class="nv"&gt;css&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nv"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;images&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;css&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;asc&lt;/span&gt;.&lt;span class="nv"&gt;gif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bg&lt;/span&gt;.&lt;span class="nv"&gt;gif&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;desc&lt;/span&gt;.&lt;span class="nv"&gt;gif&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;etc&lt;/span&gt;.&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nv"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;js&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;jquery&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;.&lt;span class="mi"&gt;9&lt;/span&gt;.&lt;span class="mi"&gt;1&lt;/span&gt;.&lt;span class="nv"&gt;min&lt;/span&gt;.&lt;span class="nv"&gt;js&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nv"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;js&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;jquery&lt;/span&gt;.&lt;span class="nv"&gt;tablesorter&lt;/span&gt;.&lt;span class="nv"&gt;min&lt;/span&gt;.&lt;span class="nv"&gt;js&lt;/span&gt;

&lt;span class="nv"&gt;mainhandler&lt;/span&gt;.&lt;span class="nv"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nv"&gt;templates&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;.&lt;span class="nv"&gt;html&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nv"&gt;app&lt;/span&gt;.&lt;span class="nv"&gt;yaml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;required&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AppEngine&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nv"&gt;main&lt;/span&gt;.&lt;span class="nv"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nv"&gt;routes&lt;/span&gt;.&lt;span class="nv"&gt;py&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="appyaml"&gt;app.yaml&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;john&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;jinja2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python27&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="n"&gt;api_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="n"&gt;threadsafe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;true&lt;/span&gt;

&lt;span class="n"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;favicon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ico&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="n"&gt;static_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;favicon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ico&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;favicon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ico&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="n"&gt;static_dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="n"&gt;static_dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="n"&gt;static_dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/.*&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;


&lt;span class="n"&gt;libraries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.5.2"&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jinja2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="mainpy"&gt;main.py&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;webapp2&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;routes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;entry_points&lt;/span&gt;

&lt;span class="c1"&gt;# must be named "application" for uwsgi webapp2, in AppEngine it should be "app"&lt;/span&gt;
&lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WSGIApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;entry_points&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="routespy"&gt;routes.py&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;webapp2&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mainhandler&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MainHandler&lt;/span&gt;

&lt;span class="c1"&gt;# Map url's to handlers in the handlers module , optionally choosing specific target method and request type&lt;/span&gt;
&lt;span class="n"&gt;entry_points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'/main'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MainHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler_method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'get'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="mainhandlerpy"&gt;mainhandler.py&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;webapp2&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jinja2&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="c1"&gt;# weird hack to ensure we go up a directory level to correctly find the templates directory&lt;/span&gt;
&lt;span class="n"&gt;jinja_environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jinja2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jinja2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileSystemLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# a list of tuples&lt;/span&gt;
        &lt;span class="n"&gt;result_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'apples'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'green'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'bananas'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'yellow'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cherries'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'red'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; 
        &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jinja_environment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'templates/main.html'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;template_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'fruits and colors'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'body_content'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'fruits and colors'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'result_list'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result_list&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'text/html'&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;template_values&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="templatesmainhtml"&gt;templates/main.html&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;xmlns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/1999/xhtml"&lt;/span&gt; &lt;span class="na"&gt;xmlns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/1999/html"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/html; charset=utf-8"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/css"&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/css/tablesorter.css"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/js/jquery-1.9.1.min.js"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/js/jquery.tablesorter.min.js"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;body_content&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"results"&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"tablesorter"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;thead&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Fruit&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Color&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;thead&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tbody&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;result_list&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endfor&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tbody&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;br&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#results"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;tablesorter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sortList&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;descending&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="non-google-app-engine-etcinitduwsgish-might-look-like"&gt;Non Google App Engine /etc/init.d/uwsgi.sh might look like:&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c1"&gt;# 2013-02-22 johnpfeiffer&lt;/span&gt;

start&lt;span class="o"&gt;(){&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;/usr/local/bin/uwsgi&lt;span class="w"&gt; &lt;/span&gt;--pidfile&lt;span class="w"&gt; &lt;/span&gt;/var/www/pidfile-uwsgi.pid&lt;span class="w"&gt; &lt;/span&gt;--touch-reload&lt;span class="o"&gt;=&lt;/span&gt;/var/www/pidfile-uwsgi.pid&lt;span class="w"&gt; &lt;/span&gt;--logto2&lt;span class="w"&gt; &lt;/span&gt;/var/www/python-john/uwsgi.log&lt;span class="w"&gt; &lt;/span&gt;--http&lt;span class="w"&gt; &lt;/span&gt;:8080&lt;span class="w"&gt; &lt;/span&gt;--wsgi-file&lt;span class="w"&gt; &lt;/span&gt;/var/www/main.py&lt;span class="w"&gt; &lt;/span&gt;--pythonpath&lt;span class="w"&gt; &lt;/span&gt;/var/www/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

stop&lt;span class="o"&gt;(){&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;kill&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-INT&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/var/www/pidfile-uwsgi.pid&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;sleep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

status&lt;span class="o"&gt;(){&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;ps&lt;span class="w"&gt; &lt;/span&gt;aux&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;uwsgi
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;
status&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;status
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
start&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;start
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
stop&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;stop
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
reload&lt;span class="p"&gt;|&lt;/span&gt;restart&lt;span class="p"&gt;|&lt;/span&gt;force-reload&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;stop
&lt;span class="w"&gt;    &lt;/span&gt;start
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
**&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; {start|stop|reload}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="alternate-flask-jinja2-example"&gt;Alternate Flask Jinja2 Example&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://bitbucket.org/johnpfeiffer/tddflask/src"&gt;https://bitbucket.org/johnpfeiffer/tddflask/src&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="programming"/><category term="jinja2"/><category term="python"/><category term="webapp2"/><category term="tablesorter.js"/></entry><entry><title>Fix Byobu infinite scroll bug on Ubuntu 12.04 Precise Pangolin</title><link href="https://blog.john-pfeiffer.com/fix-byobu-infinite-scroll-bug-on-ubuntu-1204-precise-pangolin/" rel="alternate"/><published>2013-02-15T17:36:00-08:00</published><updated>2013-02-15T17:36:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2013-02-15:/fix-byobu-infinite-scroll-bug-on-ubuntu-1204-precise-pangolin/</id><summary type="html">
&lt;p&gt;After installing Ubuntu Server 12.04 (Precise Pangolin) I was disappointed to see that one of my favorite utilities, byobu (an improvement on the classic screen = multi ssh screen with status and hotkeys), had an infinite scroll problem. &lt;/p&gt;
&lt;p&gt;(Quick, type exit before your screen disappears entirely!)&lt;/p&gt;
&lt;p&gt;Amazingly this bug shipped …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;After installing Ubuntu Server 12.04 (Precise Pangolin) I was disappointed to see that one of my favorite utilities, byobu (an improvement on the classic screen = multi ssh screen with status and hotkeys), had an infinite scroll problem. &lt;/p&gt;
&lt;p&gt;(Quick, type exit before your screen disappears entirely!)&lt;/p&gt;
&lt;p&gt;Amazingly this bug shipped in the official Ubuntu Release even though byobu 5.17 lists it as a fixed.&lt;/p&gt;
&lt;h2 id="easy-workaround-is"&gt;Easy workaround is:&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;byobu-config&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Toggle status notifications&lt;/li&gt;
&lt;li&gt;Use the arrow keys to scroll down and space bar to disable the logo &lt;/li&gt;
&lt;li&gt;Tab and Enter to Apply -&amp;gt; then Exit &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now you can safely use "byobu" on the command line in Ubuntu 12.04! (apparently not persisting in 12.04.2?)&lt;/p&gt;
&lt;p&gt;p.s. Control + a (screen mode) and then Control + a , then c ... 
now you've got multiple screens! &lt;/p&gt;
&lt;p&gt;(Control + a + 0 to go to screen 0, control + a + a to jump to the last used screen)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://help.ubuntu.com/community/Byobu"&gt;https://help.ubuntu.com/community/Byobu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Of course you can always use normal BASH navigation: &lt;a href="http://www.gnu.org/software/bash/manual/html_node/Commands-For-Moving.html"&gt;http://www.gnu.org/software/bash/manual/html_node/Commands-For-Moving.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A fix for Windows Putty users ... Window -&amp;gt; Translation -&amp;gt; UTF8 (the ISO-8859 + byobu UTF8 logo = ugh)&lt;/p&gt;
&lt;p&gt;(An untested alternate workaround: .byobu/status::tmux_left : "logo" -&amp;gt; "#logo" )&lt;/p&gt;
&lt;h2 id="update-for-12042"&gt;UPDATE for 12.04.2!&lt;/h2&gt;
&lt;p&gt;byobu in Ubuntu 12.04 uses tmux as the backend. You can change this by running byobu-select-backend and selecting screen&lt;/p&gt;
&lt;p&gt;Thanks for the tip Eric!&lt;/p&gt;
&lt;p&gt;ALTERNATE:
    .byobu/status::tmux_left : "logo" -&amp;gt; "#logo" )&lt;/p&gt;
&lt;h2 id="byobu-installation-and-basics"&gt;byobu installation and basics&lt;/h2&gt;
&lt;h3 id="installing-a-better-tmux-multiple-remote-virtual-console-screens"&gt;installing a better tmux (multiple remote virtual console screens)&lt;/h3&gt;
&lt;p&gt;BYOBU = BETTER GUI FOR SCREEN + CPU/RAM USAGE + DATETIME &lt;a href="https://help.ubuntu.com/community/Byobu"&gt;https://help.ubuntu.com/community/Byobu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(an improvement on the classic "screen" = multi ssh screen with status and hotkeys)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get install byobu
byobu-config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Toggle status notifications -&amp;gt; spacebar to disable logo -&amp;gt; Apply -&amp;gt; Exit&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;byobu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;F2 ... or control + a , then c to create a new screen&lt;/li&gt;
&lt;li&gt;F3 to move to a previous screen&lt;/li&gt;
&lt;li&gt;F4 to move to the next screen&lt;/li&gt;
&lt;li&gt;control + a , then 0 = to go to screen zero, etc.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;control + a , then a = to go to the last used screen&lt;/p&gt;
&lt;p&gt;byobu-enable&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;will have it start on every ssh connection&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;byobu-disable&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;stops auto-starting of byobu&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="if-byobu-seems-stuck-when-using-vi"&gt;If byobu seems stuck when using vi&lt;/h3&gt;
&lt;p&gt;try Control + Q  (or Control + S)&lt;/p&gt;
&lt;h3 id="moving-the-cursor-in-byobu-f7-and-beyond"&gt;Moving the cursor in byobu (f7 and beyond)&lt;/h3&gt;
&lt;p&gt;F7 = scrollback mode , vi like commands to search and copy paste&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cursor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;character&lt;/span&gt;
&lt;span class="nv"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cursor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;down&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;
&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cursor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;
&lt;span class="nv"&gt;l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cursor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;character&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;beginning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;
&lt;span class="nv"&gt;G&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Moves&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;specified&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;defaults&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;forward&lt;/span&gt;
?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;backward&lt;/span&gt;
&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Moves&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;match&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;either&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;forward&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;backword&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="screen-the-previous-generation-of-remote-virtual-console-utility"&gt;screen - the previous generation of remote virtual console utility&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get install screen
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;SSH into your machine and type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;ctrl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;
&lt;span class="n"&gt;ctrl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;disconnect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SSH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;
&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;disconnected&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dropped&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SSH&lt;/span&gt;

&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;shows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sessions&lt;/span&gt;

&lt;span class="n"&gt;There&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mf"&gt;19894.&lt;/span&gt;&lt;span class="n"&gt;pts&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="n"&gt;servername&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Attached&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Socket&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;


&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;When&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="s1"&gt;'re ready to reconnect to the last screen session&lt;/span&gt;

&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19894&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;detach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;
&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19894&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;see&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;everything&lt;/span&gt;&lt;span class="s1"&gt;'s ok&lt;/span&gt;

&lt;span class="n"&gt;ctrl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;detach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;screen&lt;/span&gt;

&lt;span class="n"&gt;who&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lost"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;
&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kill&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sessionid&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;kill&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;disconnected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;

&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19894&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;resume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;

&lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;detach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;attach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;setting the config for no visual bell!&lt;/p&gt;
&lt;p&gt;in your /home/USERNAME (or /root) directory&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;touch .screenrc

vbell off
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="linux"/><category term="byobu"/><category term="linux"/><category term="ubuntu"/></entry><entry><title>Google App Engine Python</title><link href="https://blog.john-pfeiffer.com/google-app-engine-python/" rel="alternate"/><published>2013-01-26T15:01:00-08:00</published><updated>2013-01-26T15:01:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2013-01-26:/google-app-engine-python/</id><summary type="html">
&lt;h2 id="setting-up"&gt;Setting Up&lt;/h2&gt;
&lt;p&gt;Sign up for Google App Engine (gmail + SMS verification)&lt;/p&gt;
&lt;p&gt;"Google App Engine (often referred to as GAE or simply App Engine, and also used by the acronym GAE/J) is a platform as a service (PaaS) cloud computing platform for developing and hosting web applications in Google-managed data …&lt;/p&gt;</summary><content type="html">
&lt;h2 id="setting-up"&gt;Setting Up&lt;/h2&gt;
&lt;p&gt;Sign up for Google App Engine (gmail + SMS verification)&lt;/p&gt;
&lt;p&gt;"Google App Engine (often referred to as GAE or simply App Engine, and also used by the acronym GAE/J) is a platform as a service (PaaS) cloud computing platform for developing and hosting web applications in Google-managed data centers."&lt;/p&gt;
&lt;p&gt;The Admin Dashboard is linked to your Google profile &lt;a href="https://console.cloud.google.com"&gt;https://console.cloud.google.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Login and create an Application (e.g. named john-pfeiffer reachable at &lt;a href="http://john-pfeiffer.appspot.com"&gt;http://john-pfeiffer.appspot.com&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Download and extract the SDK (e.g. gae-python.zip)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cd google_appengine
cp -a new_project_template helloworld
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Inside is the minimum file structucture required to have an application&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;app.yaml is the configuration file&lt;/li&gt;
&lt;li&gt;index.yaml is how to override configuration for database indices&lt;/li&gt;
&lt;li&gt;favicon.ico is the icon that appears in the browser tab or bookmark &lt;a href="https://en.wikipedia.org/wiki/Favicon"&gt;https://en.wikipedia.org/wiki/Favicon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;main.py is the entrypoint for your application&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h3 id="a-first-helloworldappyaml"&gt;A first helloworld/app.yaml&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;john&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pfeiffer&lt;/span&gt;
&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python27&lt;/span&gt;
&lt;span class="n"&gt;api_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;threadsafe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;yes&lt;/span&gt;

&lt;span class="n"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;favicon&lt;/span&gt;\&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ico&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;static_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;favicon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ico&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;favicon&lt;/span&gt;\&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ico&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="n"&gt;libraries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webapp2&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.5.2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;yaml files are indentation based&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="a-revised-yaml-file"&gt;A revised yaml file&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python27&lt;/span&gt;
&lt;span class="n"&gt;api_version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;threadsafe&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;yes&lt;/span&gt;

&lt;span class="n"&gt;handlers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;app&lt;/span&gt;

&lt;span class="n"&gt;libraries&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webapp2&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.5.2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the newer requirement is to NOT include the application name or version, apparently that is passed as a CLI parameter &lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/appengine/docs/standard/python/getting-started/hosting-a-static-website#creating_the_appyaml_file"&gt;https://cloud.google.com/appengine/docs/standard/python/getting-started/hosting-a-static-website#creating_the_appyaml_file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/sdk/gcloud/reference/config/set"&gt;https://cloud.google.com/sdk/gcloud/reference/config/set&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="helloworldmainpy"&gt;helloworld/main.py&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;webapp2&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hi World!'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WSGIApplication&lt;/span&gt;&lt;span class="p"&gt;([(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MainHandler&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="view-the-app-on-your-local-machine"&gt;View the app on your local machine&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cd google_appengine
./dev_appserver.py ./helloworld

    INFO 2012-12-27 04:20:20,399 dev_appserver_multiprocess.py:655]
    Running application dev\~john-pfeiffer on port 8080:
    http://localhost:8080

    INFO 2012-12-27 04:20:20,399 dev_appserver_multiprocess.py:657] Admin console is available at: http://localhost:8080/\_ah/admin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="upload-the-app-to-google-app-engine"&gt;Upload the app to Google App Engine&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;appcfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;helloworld&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;appengine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;john&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pfeiffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Starting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;john&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pfeiffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Getting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;limits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Scanning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;disk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cloning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cloning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Uploading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;blobs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Uploaded&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;blobs&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Compilation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;starting&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Compilation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Starting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deployment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Checking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;succeeded&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;again&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Checking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;succeeded&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;again&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Checking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;succeeded&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Deployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;successful&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Verify with &lt;code&gt;curl http://john-pfeiffer.appspot.com&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="download-an-existing-application-from-google-app-engine"&gt;Download an existing application from Google App Engine&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myapp&lt;/span&gt;
&lt;span class="n"&gt;appcfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;download_app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;john&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pfeiffer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myapp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;This will use OAuth 2 to open a browser/give you a link where you can confirm the action
Once confirmed it will download the latest version off your application code and files
This is independent of and not a replacement for version control!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="updating-the-application-for-routes-and-post-requests"&gt;Updating the Application for routes and POST requests&lt;/h2&gt;
&lt;p&gt;It is easy to use the MVC pattern while inheriting from the framework &lt;a href="https://webapp-improved.appspot.com/guide/handlers.html"&gt;https://webapp-improved.appspot.com/guide/handlers.html&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="appyaml"&gt;app.yaml&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that the version has to be explicitly updated in order to deploy something new&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;john&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;pfeiffer&lt;/span&gt;
&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python27&lt;/span&gt;
&lt;span class="n"&gt;api_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;threadsafe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;yes&lt;/span&gt;

&lt;span class="n"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;favicon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ico&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;static_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;favicon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ico&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;favicon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ico&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/.*&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="n"&gt;libraries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webapp2&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.5.2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="mainpy"&gt;main.py&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;webapp2&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'hello John Pfeiffer!'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PageOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;
&lt;span class="s2"&gt;            PageOne&lt;/span&gt;
&lt;span class="s2"&gt;        """&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PageTwo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PageTwo'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;get_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;
        &lt;span class="n"&gt;post_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_values&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WSGIApplication&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MainHandler&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/page-one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PageOne&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/page-two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PageTwo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;run_wsgi_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="update-the-google-app-engine-application-code-that-is-running-in-the-cloud"&gt;Update the google app engine application code that is running in the cloud&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;./appcfg.py update john-pfeiffer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;update does not publish the new version yet&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;./appcfg.py set_default_version john-pfeiffer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;or you can use the WebUI dashboard to change the default or delete a Version for an Application Instance&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://cloud.google.com/appengine/docs/python/config/appref"&gt;https://cloud.google.com/appengine/docs/python/config/appref&lt;/a&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id="a-simple-single-file-crud-app-using-the-google-appengine-database"&gt;A simple single file CRUD app using the Google AppEngine Database&lt;/h2&gt;
&lt;p&gt;Google AppEngine applications can leverage the platforms NoSQL database &lt;a href="https://cloud.google.com/appengine/docs/python/datastore/"&gt;https://cloud.google.com/appengine/docs/python/datastore/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The example application below also shows how to override the default 404 and 500 errors with custom jinja2 templates which would require installing the jinja2 dependency and an extra subdirectory named templates with the HTML&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.john-pfeiffer.com/jinja2-a-web-html-template-layout-for-everyone/"&gt;https://blog.john-pfeiffer.com/jinja2-a-web-html-template-layout-for-everyone/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/appengine/docs/python/ndb/"&gt;https://cloud.google.com/appengine/docs/python/ndb/&lt;/a&gt; has improved and deprecated the DB Datastore library used below&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c1"&gt;# 2013-01-20 johnpfeiffer&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;traceback&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;cgi&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;webapp2&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jinja2&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.appengine.ext&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;

&lt;span class="n"&gt;jinja_environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jinja2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;jinja2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileSystemLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="vm"&gt;__file__&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringProperty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringProperty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringProperty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTimeProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;auto_now_add&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# TODO: navigation bar&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"""Welcome &amp;lt;br/&amp;gt;&lt;/span&gt;

&lt;span class="s2"&gt;         &amp;lt;form action="/listnodes" method="get"&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;             &amp;lt;input type="submit" value="List Nodes"&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;         &amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;         &amp;lt;br/&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;         &amp;lt;form action="/createnodeform" method="get"&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;             &amp;lt;input type="submit" value="Create Node"&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;         &amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;         &amp;lt;form action="/deletenode" method="post"&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;             &amp;lt;label&amp;gt;id&amp;lt;/label&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type="text" id="id" name="id" /&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;             &amp;lt;input type="submit" value="Delete Node"&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;         &amp;lt;/form&amp;gt;&lt;/span&gt;

&lt;span class="s2"&gt;         """&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ListNodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Welcome &amp;lt;br/&amp;gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; , &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; , &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; ( &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; )&amp;lt;br/&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"id"&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"parent_id"&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"created"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; , &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; , &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; ( &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; )&amp;lt;br/&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# TODO: use template system&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Create uses POST'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;post_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;

    &lt;span class="c1"&gt;# todo extract to helper for input validation and sanitization&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"nodename"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"id"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"parentid"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'ERROR: DEBUG:'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_values&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parent_id&lt;/span&gt;

        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'successfully created: '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"""&lt;/span&gt;
&lt;span class="s2"&gt;         &amp;lt;form action="/listnodes" method="get"&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;             &amp;lt;input type="submit" value="List Nodes"&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;         &amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;        """&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateNodeForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"""&lt;/span&gt;
&lt;span class="s2"&gt;         &amp;lt;form action="/createnode" method="post"&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;            &amp;lt;table&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;                &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;label&amp;gt;node name&amp;lt;/label&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type="text" id="nodename" name="nodename" /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;                &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;label&amp;gt;id&amp;lt;/label&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type="text" id="id" name="id" /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;                &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;label&amp;gt;parent id&amp;lt;/label&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type="text" id="parentid" name="parentid" /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;                &amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type="submit"&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;            &amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;          &amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="s2"&gt;        """&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# TODO: use template system&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeleteNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Delete uses POST'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;post_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;

    &lt;span class="c1"&gt;# todo extract to helper for input validation and sanitization&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"id"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;#    q = db.Query( Node )    # keys_only is faster and cheaper than retrieving the entities&lt;/span&gt;
&lt;span class="c1"&gt;#    q.filter( "id=" , id )&lt;/span&gt;
    &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;keys_only&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"id="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;node_to_delete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;#    db.delete( )&lt;/span&gt;
&lt;span class="c1"&gt;#    node_key =          # __key__&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; , &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; , &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; ( &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; )&amp;lt;br/&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"parent_id"&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node_to_delete&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;


    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;



&lt;span class="c1"&gt;# url handler below -----------------------------&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webapp2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WSGIApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MainPage&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'/listnodes'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ListNodes&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'/createnode'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateNode&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'/createnodeform'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateNodeForm&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'/deletenode'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DeleteNode&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_404&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;template_dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'ERROR 404'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'body_content'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jinja_environment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_template&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'templates/error.html'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;template_dictionary&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_status&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_int&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_500&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;traceback&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print_exc&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;template_dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Meow'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'body_content'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Meow.  Meow meow meow, meow meow.'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jinja_environment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_template&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'templates/error.html'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;template_dictionary&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_status&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_handlers&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;handle_404&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_handlers&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;handle_500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id="pointing-dns-and-your-domain-to-your-appengine-app"&gt;Pointing DNS and your domain to your appengine app&lt;/h1&gt;
&lt;p&gt;If you want to have your appengine url be customized with a nice domain name you will need to have your registrar (like Namecheap =) point the DNS towards google:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://cloud.google.com/appengine/docs/standard/python/mapping-custom-domains"&gt;https://cloud.google.com/appengine/docs/standard/python/mapping-custom-domains&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="programming"/><category term="google app engine"/><category term="python"/><category term="webapp2"/></entry><entry><title>Server Operations: Cloud versus Build Your Own</title><link href="https://blog.john-pfeiffer.com/server-operations-cloud-versus-build-your-own/" rel="alternate"/><published>2012-11-08T17:06:00-08:00</published><updated>2012-11-08T17:06:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2012-11-08:/server-operations-cloud-versus-build-your-own/</id><summary type="html">&lt;p&gt;Here's my response to Jeff Atwood's calculations and incorrect conclusion about Building Your Own Server: I'm not sure I know when an organization's Production deployment doesn't need reduced
complexity/costs (people!), flexibility for load, and redundancy...&lt;/p&gt;
&lt;p&gt;"Anyway, I want to make it clear that building and colocating your own servers …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Here's my response to Jeff Atwood's calculations and incorrect conclusion about Building Your Own Server: I'm not sure I know when an organization's Production deployment doesn't need reduced
complexity/costs (people!), flexibility for load, and redundancy...&lt;/p&gt;
&lt;p&gt;"Anyway, I want to make it clear that building and colocating your own servers isn't (always) crazy, it isn't scary, heck, it isn't even particularly hard. In some situations it can make sense to build and rack your own servers, provided ...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you want absolute top of the line server performance without paying thousands of dollars per month for the privilege&lt;/li&gt;
&lt;li&gt;you are willing to invest the time in building, racking, and configuring your servers&lt;/li&gt;
&lt;li&gt;you have the capital to invest up front&lt;/li&gt;
&lt;li&gt;you desire total control over the hardware&lt;/li&gt;
&lt;li&gt;you aren't worried about the flexibility of quickly provisioning new servers to handle unanticipated load&lt;/li&gt;
&lt;li&gt;you don't need the redundancy, geographical backup, and flexibility that comes with cloud virtualization"&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="http://www.codinghorror.com/blog/2012/10/building-servers-for-fun-and-prof-ok-maybe-just-for-fun.html"&gt;http://www.codinghorror.com/blog/2012/10/building-servers-for-fun-and-prof-ok-maybe-just-for-fun.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Hi Jeff, long time fan, first time commenter... I love building servers too and I've managed a small group of servers, I personally use Linode, and my currently company uses AWS and some internal servers...&lt;/p&gt;
&lt;p&gt;You would agree that in coding you pick the right tool for the job (scientific computing would use a different technology stack than a standard ecommerce startup website)...&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;AWS is elastic (you pay a premium for being to scale up or down - and there's value to the agility with which you can change or add new services)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;AWS RDS is a huge improvement over managing MySQL replication, and they have Elastic Load Balancing and lots of other addons that take serious Ops chops to create and maintain&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Server operations cost is not the raw hardware:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The biggest cost in Ops is people (same as coding), so leveraging Amazon saves on how many people you need to pay to manage your server farm (yes, SysAdmins take holidays and change jobs so cost = N+1 )... you can outsource half way by colocating but setting up the redundancy, monitoring, auto scaling, etc. becomes a physical pain (you want West Coast and East Coast  servers, right?).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The infrastructure of cooling, UPS, network (bandwidth!), backups, etc. is also a big factor in Operations (does your server room have building security? a backup generator?)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;My point is that for a stealth mode startup or any internal lab testing buying servers is a no brainer - do it with ESXi or OpenStack and hack away!&lt;/p&gt;
&lt;p&gt;BUT for Production you'll need some Cloud strategy (AWS competitors: RedHat OpenShift, RackSpace Cloud, IBM, ATT Compute, Google AppEngine, etc. means lower prices and improved services)&lt;/p&gt;
&lt;p&gt;Unless, as you've already mentioned, "if you happen to have hanging around a pile of cash and tech expertise that's underutilized..."&lt;/p&gt;</content><category term="it"/><category term="cloud"/><category term="startup planning"/><category term="ops"/></entry><entry><title>Tomcat deployment on Openshift for free</title><link href="https://blog.john-pfeiffer.com/tomcat-deployment-on-openshift-for-free/" rel="alternate"/><published>2012-09-11T13:48:00-07:00</published><updated>2012-09-11T13:48:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2012-09-11:/tomcat-deployment-on-openshift-for-free/</id><summary type="html">
&lt;p&gt;openshift is the cloud PaaS offering from RedHat&lt;/p&gt;
&lt;h3 id="prerequisites-and-dependencies"&gt;Prerequisites and dependencies&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;update&lt;/span&gt;
&lt;span class="nv"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ruby1&lt;/span&gt;.&lt;span class="mi"&gt;9&lt;/span&gt;.&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;git&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;core&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nv"&gt;yay&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ubuntu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;.&lt;span class="mi"&gt;04&lt;/span&gt;

&lt;span class="nv"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;gem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;rhc&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nv"&gt;red&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;openshift&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="openshift-client-tools-setup"&gt;OpenShift Client tools setup&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rhc setup&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;/.&lt;/span&gt;&lt;span class="n"&gt;openshift …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">
&lt;p&gt;openshift is the cloud PaaS offering from RedHat&lt;/p&gt;
&lt;h3 id="prerequisites-and-dependencies"&gt;Prerequisites and dependencies&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;update&lt;/span&gt;
&lt;span class="nv"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ruby1&lt;/span&gt;.&lt;span class="mi"&gt;9&lt;/span&gt;.&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;git&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;core&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nv"&gt;yay&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ubuntu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;.&lt;span class="mi"&gt;04&lt;/span&gt;

&lt;span class="nv"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;gem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;rhc&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nv"&gt;red&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;openshift&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="openshift-client-tools-setup"&gt;OpenShift Client tools setup&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rhc setup&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;/.&lt;/span&gt;&lt;span class="n"&gt;openshift&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;express&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;express&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transferred&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;different&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;computers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SSH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;were&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;We&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;such&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;
&lt;span class="n"&gt;Created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;/.&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;id_rsa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pub&lt;/span&gt;

&lt;span class="n"&gt;Your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uploaded&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OpenShift&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Would&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="rhc-commands"&gt;rhc commands&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rhc -h&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rhc domain show&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;prompts for password&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;rhc app create -h&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Valid application types are (nodejs-0.6, ruby-1.9, jbossas-7, python-2.6, jenkins-1.4, ruby-1.8, jbosseap-6.0, diy-0.1, php-5.3, perl-5.10)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rhc app create -a john -t diy-0.1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rhc app show -a john&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rhc app cartridge list&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="your-local-git-repo"&gt;Your local git repo&lt;/h3&gt;
&lt;p&gt;ON YOUR LOCAL MACHINE BROWSE TO WHERE YOU WANT TO STORE YOUR GIT REPO&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git clone ssh://a261d0fc2932413694456e3473fdc972@APPNAME-DOMAIN.rhcloud.com/~/git/...&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git status&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git pull&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="repo-layout-of-johnrepo"&gt;REPO LAYOUT of ~/john/repo&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;.openshift/action_hooks/start&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;Script&lt;span class="w"&gt; &lt;/span&gt;that&lt;span class="w"&gt; &lt;/span&gt;gets&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;application&lt;span class="w"&gt;  &lt;/span&gt;
.openshift/action_hooks/stop&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;Script&lt;span class="w"&gt; &lt;/span&gt;that&lt;span class="w"&gt; &lt;/span&gt;gets&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;stop&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;application&lt;span class="w"&gt;  &lt;/span&gt;
.openshift/action_hooks/pre_build&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;Script&lt;span class="w"&gt; &lt;/span&gt;that&lt;span class="w"&gt; &lt;/span&gt;gets&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;every&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;push&lt;span class="w"&gt; &lt;/span&gt;before&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt;  &lt;/span&gt;
.openshift/action_hooks/build&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;Script&lt;span class="w"&gt; &lt;/span&gt;that&lt;span class="w"&gt; &lt;/span&gt;gets&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;every&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;push&lt;span class="w"&gt; &lt;/span&gt;as&lt;span class="w"&gt; &lt;/span&gt;part&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;process&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;on&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;CI&lt;span class="w"&gt; &lt;/span&gt;system&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;available&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
.openshift/action_hooks/deploy&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;Script&lt;span class="w"&gt; &lt;/span&gt;that&lt;span class="w"&gt; &lt;/span&gt;gets&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;every&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;push&lt;span class="w"&gt; &lt;/span&gt;after&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;but&lt;span class="w"&gt; &lt;/span&gt;before&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;app&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;restarted&lt;span class="w"&gt;  &lt;/span&gt;
.openshift/action_hooks/post_deploy&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;Script&lt;span class="w"&gt; &lt;/span&gt;that&lt;span class="w"&gt; &lt;/span&gt;gets&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;every&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;push&lt;span class="w"&gt; &lt;/span&gt;after&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;app&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;restarted

diy&lt;span class="w"&gt;  &lt;/span&gt;
misc&lt;span class="w"&gt;  &lt;/span&gt;
README&lt;span class="w"&gt;  &lt;/span&gt;
static/&lt;span class="w"&gt; &lt;/span&gt;If&lt;span class="w"&gt; &lt;/span&gt;it&lt;span class="w"&gt; &lt;/span&gt;exists&lt;span class="w"&gt; &lt;/span&gt;externally&lt;span class="w"&gt; &lt;/span&gt;exposed&lt;span class="w"&gt; &lt;/span&gt;static&lt;span class="w"&gt; &lt;/span&gt;content&lt;span class="w"&gt; &lt;/span&gt;goes&lt;span class="w"&gt; &lt;/span&gt;here
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;CHANGES ARE ONE DIRECTIONAL FROM THE GIT CLONE TO THE OPENSHIFT BOX&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mv diy/testrubyserver.rb ../misc&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mv diy/index.html ../misc&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git add -A&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git commit -m "moved initial test stuff to /misc"&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git push&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;if the app is running then a git push automatically ...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Counting objects: 6, done.
Delta compression using up to 4 threads.  
Compressing objects: 100% (4/4), done.  
Writing objects: 100% (4/4), 607 bytes, done.  
Total 4 (delta 1), reused 1 (delta 0)  
remote: Stopping application...  
remote: Done  
remote: ~/git/john.git ~/git/john.git  
remote: ~/git/john.git
remote: Running .openshift/action_hooks/pre_build  
remote: Running .openshift/action_hooks/build  
remote: Running .openshift/action_hooks/deploy  
remote: Starting application...  
remote: Done  
remote: Running .openshift/action_hooks/post_deploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;ssh 33d90ea45fd91e42096651d937e@john-pfeiffer.rhcloud.com&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;//note that HOME is /var/lib/stickshift/12315longidentifier  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;cd app-root/data&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wget http://mirror.cc.columbia.edu/pub/software/apache/tomcat/tomcat-7/v7.0.29/bin/apache-tomcat-7.0.29.tar.gz&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;tar -xzvf apache-tomcat-7.0.29.tar.gz&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;expands to 13MB, note that JAVA is already installed by default&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;rm apache-tomcat-7.0.29.tar.gz&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="openshift-ports-and-proxy"&gt;Openshift ports and proxy&lt;/h3&gt;
&lt;p&gt;Since OpenShift has a proxy setup that passes port 80 to your local server on port 8080, BUT  &lt;/p&gt;
&lt;p&gt;OpenShift does not allow users to bind to any port below 15000 other than 8080, so...&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ssh 33d90ea45fd91e42096651d937e@john-pfeiffer.rhcloud.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;env&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;shows you all of the environment variables in the openShift&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;multitenant config&lt;/p&gt;
&lt;p&gt;&lt;code&gt;env | grep INTERNAL&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;OPENSHIFT_INTERNAL_PORT=8080&lt;br/&gt;
OPENSHIFT_INTERNAL_IP=127.13.29.1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;vi app-root/data/apache-tomcat-7.0.29/conf/server.xml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;escape key then :x (save and quit)&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;&lt;code&gt;cd app-root/data/apache-tomcat-7.0.29/bin&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sh startup.sh &amp;amp;&amp;amp; tail -f ../logs/*&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;(this is how you can start it manually from within ssh, you'll have to stop it manually too!)&lt;/p&gt;
&lt;p&gt;ADDING Tomcat to your default start and stop scripts (which are used during every git push)&lt;/p&gt;
&lt;p&gt;In your local git repo there is a hidden directory ".openshift"&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cd APPNAME/.openshift/action_hooks&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="vi-openshiftaction_hooksstart"&gt;vi .openshift/action_hooks/start&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;#nohup $OPENSHIFT_REPO_DIR/diy/testrubyserver.rb&lt;/span&gt;
&lt;span class="nv"&gt;$OPENSHIFT_INTERNAL_IP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OPENSHIFT_REPO_DIR&lt;/span&gt;/diy&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OPENSHIFT_LOG_DIR&lt;/span&gt;/server.log&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OPENSHIFT&lt;/span&gt;&lt;span class="se"&gt;\_&lt;/span&gt;DATA&lt;span class="se"&gt;\_&lt;/span&gt;DIR/apache-tomcat-7.0.29/bin
nohup&lt;span class="w"&gt; &lt;/span&gt;sh&lt;span class="w"&gt; &lt;/span&gt;startup.sh
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"completed tomcat7 startup"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="vi-openshiftaction_hooksstop"&gt;vi .openshift/action_hooks/stop&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OPENSHIFT_DATA_DIR&lt;/span&gt;/apache-tomcat-7.0.29/bin&lt;span class="w"&gt;  &lt;/span&gt;
nohup&lt;span class="w"&gt; &lt;/span&gt;sh&lt;span class="w"&gt; &lt;/span&gt;shutdown.sh&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"completed tomcat7 shutdown"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;git add -A&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;`git commit -m "removed testrubyserver.rb and added tomcat to start/stop scripts"  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;git push&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="http://APPNAME-DOMAINNAME.rhcloud.com"&gt;http://APPNAME-DOMAINNAME.rhcloud.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="autodeploy-the-latest"&gt;Autodeploy the latest&lt;/h3&gt;
&lt;p&gt;MOVE YOUR WEBAPPS DIRECTORY TO THE GIT REPO SO THAT A GIT PUSH WILL AUTO DEPLOY THE NEWEST&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mv $OPENSHIFT_DATA_DIR/apache-tomcat-7.0.29/webapps ~/john/repo/diy/webapps&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ln -s ~/john/repo/diy/webapps webapps&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Strongly advised to remove the manager and example apps (just deploy your .war's)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rhc app stop -a APPNAME -p YOURPASSWORD&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;yes, it uses your RHCloud account username and password for app management&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;ssh 33d90ea45fd91e42096651d937e@john-pfeiffer.rhcloud.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mv ~/app-root/data/apache-tomcat-7.0.29/webapps/* app-root/repo/misc&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mv app-root/repo/misc/ROOT ~/app-root/data/apache-tomcat-7.0.29/webapps&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;rhc app start -a APPNAME -p YOURPASSWORD&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="once-youve-sshd-in"&gt;ONCE YOU'VE SSH'D IN...&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;help&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ps|ls|
ctl_app start [stop|restart|status]
mysql | mongo | quota
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;NOTE: sometimes it's easier to use a UI &lt;a href="https://openshift.redhat.com/app/console/applications"&gt;https://openshift.redhat.com/app/console/applications&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My Account -&amp;gt; Public Keys&lt;/p&gt;
&lt;p&gt;My Applications -&amp;gt; APPLICATION_NAME -&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rhc app add-alias -a myapp --alias myapp.net&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="future-thoughts"&gt;Future Thoughts&lt;/h2&gt;
&lt;p&gt;Eclipse + m2e (maven plugin) + jetty plugin for fast and easy dependency management -&amp;gt; mvn install + added custom script can put your .war into your local openshift repo for continuous deployment.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.openshift.com/"&gt;https://www.openshift.com/&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="cloud"/><category term="paas"/><category term="openshift"/><category term="tomcat"/></entry><entry><title>Creating Economies of Scale in Software Development</title><link href="https://blog.john-pfeiffer.com/creating-economies-of-scale-in-software-development/" rel="alternate"/><published>2012-07-29T04:39:00-07:00</published><updated>2012-07-29T04:39:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2012-07-29:/creating-economies-of-scale-in-software-development/</id><summary type="html">
&lt;h3 id="planning-to-be-a-successful-software-company"&gt;Planning to be a successful software company&lt;/h3&gt;
&lt;p&gt;Economies of Scale in Software Development is about planning to be successful.&lt;/p&gt;
&lt;p&gt;Software development is rather cheap when compared to physical manufacturing but that doesn't mean it doesn't cost money to create a product or service.&lt;/p&gt;
&lt;p&gt;The classic myth of the lone wolf …&lt;/p&gt;</summary><content type="html">
&lt;h3 id="planning-to-be-a-successful-software-company"&gt;Planning to be a successful software company&lt;/h3&gt;
&lt;p&gt;Economies of Scale in Software Development is about planning to be successful.&lt;/p&gt;
&lt;p&gt;Software development is rather cheap when compared to physical manufacturing but that doesn't mean it doesn't cost money to create a product or service.&lt;/p&gt;
&lt;p&gt;The classic myth of the lone wolf hacker who creates a viral product runs counter to the reality of billions of dollars generated by the software industry.&lt;/p&gt;
&lt;h3 id="elements-of-a-software-team"&gt;Elements of a Software Team&lt;/h3&gt;
&lt;p&gt;Consider the many elements required to create a software product:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Developers (hiring, location, communication, etc.)  &lt;/li&gt;
&lt;li&gt;Developer tools (IDE, compiler, debugger, etc.)  &lt;/li&gt;
&lt;li&gt;QA (you do run tests of some sort, don't you?!?!)  &lt;/li&gt;
&lt;li&gt;Distribution/Release (shrink wrapped or delivered by Internet continuously, it still costs something)  &lt;/li&gt;
&lt;li&gt;Support (assuming you've actually got users)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="making-software-better-faster-cheaper"&gt;Making Software Better, Faster, Cheaper&lt;/h3&gt;
&lt;p&gt;What can you do to make your software cheaper, faster, and most importantly, better?&lt;/p&gt;
&lt;h4 id="1-use-open-source"&gt;1. Use Open Source&lt;/h4&gt;
&lt;p&gt;(like facebook, twitter, google, netflix). Leverage the crowd and reduce your costs (e.g. not buying software licenses, not paying someone to audit your software licenses, etc.)&lt;/p&gt;
&lt;p&gt;Spending money on licenses becomes cost prohibitive for large successful organizations.&lt;/p&gt;
&lt;p&gt;More importantly, get the quality of years of development by lots of different perspectives. The homogeneous gets wiped out by a single cause, get diversified!&lt;/p&gt;
&lt;p&gt;Concerns about competitive advantage based on your supply chain (e.g. the myth "using Open Source tools doesn't give you an edge") are unfounded as:&lt;/p&gt;
&lt;p&gt;a. If your competitive advantage is a "secret supplier" then your business will go bust as soon as anyone else finds out your secret.&lt;/p&gt;
&lt;p&gt;b. If you're paranoid enough to worry your competitors are messing with your tools then you shouldn't trust a closed source vendor - keep it in the open where everyone's watching.&lt;/p&gt;
&lt;p&gt;Giving back to open source projects you depend on has a multiplier effect: a healthy community project is far cheaper than paying full time to support your critical requirements and has built in support and marketing.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://readwrite.com/2013/10/17/is-facebook-the-worlds-largest-open-source-company"&gt;http://readwrite.com/2013/10/17/is-facebook-the-worlds-largest-open-source-company&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.cnet.com/news/worlds-biggest-open-source-company-google/"&gt;http://www.cnet.com/news/worlds-biggest-open-source-company-google/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://techblog.netflix.com/2010/12/why-we-use-and-contribute-to-open.html"&gt;http://techblog.netflix.com/2010/12/why-we-use-and-contribute-to-open.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="2-standardization"&gt;2. Standardization&lt;/h4&gt;
&lt;p&gt;Consider the trend of large clusters of commodity hardware. Using the same internal development tools means it's easier to focus on the real problems and not get lost in translation.&lt;/p&gt;
&lt;p&gt;This doesn't mean sticking to old versions or being afraid to innovate, but make success a formula and not an accident.&lt;/p&gt;
&lt;p&gt;Larger efficiencies can be generated by getting a decent level of determinism in the workflow.&lt;/p&gt;
&lt;p&gt;This means use the same IDE, the same dependency libraries, communication channels, etc. If you have rock stars that can't learn the common tool or won't teach others why their method is better then you run the risk of a dysfunctional "all-star" time bomb.&lt;/p&gt;
&lt;p&gt;This also applies to fairness: office perks, salaries, etc. Make it an organization about transparent achievable results, not a labyrinth of back room exceptions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://techblog.netflix.com/2011/08/building-with-legos.html"&gt;http://techblog.netflix.com/2011/08/building-with-legos.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.quora.com/Why-hasn-t-Facebook-migrated-away-from-PHP"&gt;http://www.quora.com/Why-hasn-t-Facebook-migrated-away-from-PHP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://spectrum.ieee.org/telecom/internet/under-the-hood-at-google-and-facebook"&gt;http://spectrum.ieee.org/telecom/internet/under-the-hood-at-google-and-facebook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.infoq.com/presentations/Development-at-Google"&gt;http://www.infoq.com/presentations/Development-at-Google&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="3-reduced-specialization-and-silos"&gt;3. Reduced specialization (and silos)&lt;/h4&gt;
&lt;p&gt;System Administrator's whose sole purpose is to watch machines is a dying breed. &lt;/p&gt;
&lt;p&gt;An army of QA who manually walk through the same test plan over and over is also legacy. &lt;/p&gt;
&lt;p&gt;Start with "DRY - Do Not Repeat Yourself".&lt;/p&gt;
&lt;p&gt;Develop automation early and make the process simple and obvious. &lt;/p&gt;
&lt;p&gt;(i.e. Amazon's internal conversion to APIs &lt;a href="http://apievangelist.com/2012/01/12/the-secret-to-amazons-success-internal-apis"&gt;http://apievangelist.com/2012/01/12/the-secret-to-amazons-success-internal-apis&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;This also means avoiding purchasing hardware (and the associated inventory/maintenance/overhead).&lt;/p&gt;
&lt;p&gt;The more virtual servers you buy the larger volume discount you can negotiate. It's not entirely about cost: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;you're renting somebody else's implementation of best practice &lt;/li&gt;
&lt;li&gt;you need to make your product number one, distractions from that reduce velocity.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your feature set will continually grow. (Even with careful pruning). &lt;/p&gt;
&lt;p&gt;By looking at software as the solution to quality/delivery/maintenance/etc. you can reduce your running cost to a fraction of what it would be AND be able scale up quickly when you become wildly successful.&lt;/p&gt;
&lt;p&gt;Note: people are still irreplaceable but by having a lot more Developers (and less unique specialists) you can distribute the load more evenly.&lt;/p&gt;
&lt;p&gt;Getting more cross functional individuals means less silos and less communication gaps. &lt;/p&gt;
&lt;p&gt;The network mesh effect destroys productivity if everyone's a separate bottleneck.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.learningapi.com/blog/archives/000079.html"&gt;http://www.learningapi.com/blog/archives/000079.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://techblog.netflix.com/2012/06/netflix-operations-part-i-going.html"&gt;http://techblog.netflix.com/2012/06/netflix-operations-part-i-going.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.infoq.com/articles/sw-eating-silos"&gt;http://www.infoq.com/articles/sw-eating-silos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hbr.org/2011/12/quiet-but-unsubtle-innovation.html"&gt;https://hbr.org/2011/12/quiet-but-unsubtle-innovation.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="4-make-it-real"&gt;4. Make it real&lt;/h4&gt;
&lt;p&gt;Code that lives solves problems and is valuable. &lt;/p&gt;
&lt;p&gt;Uncommitted, unused, and otherwise un-useful code costs money to debug and deconstruct; much worse it costs time.&lt;/p&gt;
&lt;p&gt;Continuous integration gets your stable unit tested code some real world bruises. Continuous deployment gets your code crunching data and making users happy.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://royal.pingdom.com/2010/06/18/the-software-behind-facebook/"&gt;http://royal.pingdom.com/2010/06/18/the-software-behind-facebook/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.infoq.com/presentations/cd-linkedin"&gt;http://www.infoq.com/presentations/cd-linkedin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="critical-leadership"&gt;Critical Leadership&lt;/h3&gt;
&lt;p&gt;There will be bugs. &lt;/p&gt;
&lt;p&gt;Far more important are to ensure the product fits the market and have the ability to quickly fix what's broken - sometimes amputation due to business needs is critical (i.e. how Flickr the photo sensation was born &lt;a href="http://en.m.wikipedia.org/wiki/Flickr"&gt;http://en.m.wikipedia.org/wiki/Flickr&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Your team must be able to execute. &lt;/p&gt;
&lt;p&gt;You lead them by example (both the late nights and the high fives) and these real people will deliver exponential success.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.fastcompany.com/3044952/hit-the-ground-running/how-bill-gates-andy-grove-and-steve-jobs-found-success-without-busine"&gt;http://www.fastcompany.com/3044952/hit-the-ground-running/how-bill-gates-andy-grove-and-steve-jobs-found-success-without-busine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hbr.org/2012/04/the-real-leadership-lessons-of-steve-jobs"&gt;https://hbr.org/2012/04/the-real-leadership-lessons-of-steve-jobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.kissmetrics.com/brian-chesky-alfred-lin-culture/"&gt;https://blog.kissmetrics.com/brian-chesky-alfred-lin-culture/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.fastcompany.com/3007268/where-are-they-now/not-happy-accident-how-google-deliberately-designs-workplace-satisfaction"&gt;http://www.fastcompany.com/3007268/where-are-they-now/not-happy-accident-how-google-deliberately-designs-workplace-satisfaction&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="it"/><category term="Software"/><category term="Thoughts"/><category term="leadership"/><category term="management"/></entry><entry><title>An Evolution of Questions and Answers</title><link href="https://blog.john-pfeiffer.com/an-evolution-of-questions-and-answers/" rel="alternate"/><published>2012-06-21T17:46:00-07:00</published><updated>2012-06-21T17:46:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2012-06-21:/an-evolution-of-questions-and-answers/</id><summary type="html">&lt;p&gt;Socrates oral questions (and answers) were immortalized by Plato's writings.&lt;/p&gt;
&lt;p&gt;I love books, the distillation of the wisdom of the ages, and the libraries where they are enshrined and shared. &lt;/p&gt;
&lt;p&gt;Visiting a library is an exciting insight into the culture and values of a location.&lt;/p&gt;
&lt;p&gt;Encyclopedias, Dictionaries, and all manner …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Socrates oral questions (and answers) were immortalized by Plato's writings.&lt;/p&gt;
&lt;p&gt;I love books, the distillation of the wisdom of the ages, and the libraries where they are enshrined and shared. &lt;/p&gt;
&lt;p&gt;Visiting a library is an exciting insight into the culture and values of a location.&lt;/p&gt;
&lt;p&gt;Encyclopedias, Dictionaries, and all manner of reference books were the compendium of answers; questions had to be researched (sometimes
involving extensive legwork, detective work, and persistence).&lt;/p&gt;
&lt;p&gt;Digitization of books (e.g. encyclopedia on cd-rom) enabled huge improvement in the access and distribution of information.&lt;/p&gt;
&lt;p&gt;Physical bulletin boards and community centers are also places of information exchange; digital communication (modems!) transformed the reach and capacity.&lt;/p&gt;
&lt;p&gt;Television and Video, despite many attempts, has long remained a one way medium. (Though too there's something inherently relaxing about passive entertainment).&lt;/p&gt;
&lt;p&gt;The Internet (and especially Google search) means specialist websites and esoteric questions are a couple links away; it has also enabled crowd sourcing answers.&lt;/p&gt;
&lt;p&gt;I'm amazed at the speed and accuracy of answers aggregated by Wikipedia, StackExchange, Quora, etc. (which admittedly still require filtering and research for correctness and comprehension).&lt;/p&gt;
&lt;p&gt;It's gotten so that now I receive answers to questions which I didn't realize I had! &lt;/p&gt;</content><category term="it"/><category term="Information"/><category term="Thoughts"/></entry><entry><title>Mid 2012 technology and business prediction for 2013 and beyond</title><link href="https://blog.john-pfeiffer.com/mid-2012-technology-and-business-prediction-for-2013-and-beyond/" rel="alternate"/><published>2012-05-31T16:00:00-07:00</published><updated>2012-05-31T16:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2012-05-31:/mid-2012-technology-and-business-prediction-for-2013-and-beyond/</id><summary type="html">&lt;p&gt;The well known perceived future business model is constant (mobile) targeted (user profiling) advertising (everywhere) with instant purchase (online + mobile) and delivery (streaming)&lt;/p&gt;
&lt;p&gt;So Apple (Android) + Facebook + Google + Amazon + (Netflix?) , right?&lt;/p&gt;
&lt;p&gt;I think there's more to think about: the infrastructure is the gold; Hardware Manufacturers (CPU + RAM + SSD), Servers, Routers …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The well known perceived future business model is constant (mobile) targeted (user profiling) advertising (everywhere) with instant purchase (online + mobile) and delivery (streaming)&lt;/p&gt;
&lt;p&gt;So Apple (Android) + Facebook + Google + Amazon + (Netflix?) , right?&lt;/p&gt;
&lt;p&gt;I think there's more to think about: the infrastructure is the gold; Hardware Manufacturers (CPU + RAM + SSD), Servers, Routers, ISP's, Content Producers, Transaction Processing, Data and User Platforms.&lt;/p&gt;
&lt;p&gt;The new ecosystem is inherently more distributed (more users globally, more devices) and fragmented (more OS, Apps, Content Producers, etc.) than the "Microsoft Era".&lt;/p&gt;
&lt;p&gt;Complexity increases with more data to aggregate/index/filter and many smaller transactions: consider previously purchasing a single desktop, monitor, (Windows bundled with it) and MS Word + one or two apps (Quickbooks or Photoshop).&lt;/p&gt;
&lt;p&gt;Now it's a laptop + phone + tablet (Windows + Android + iOS), each with many apps (including online services like Facebook) and the challenge of maintaining a consistent view of security, data, and even application state.&lt;/p&gt;
&lt;p&gt;Business purchases used to be larger, even grouped for volume (i.e. a company orders 1,000 desktops + monitors at $1,500 a piece); now it's bring your own device ($500) and many small app and content purchases for 99 cents to $10.&lt;/p&gt;
&lt;p&gt;Companies that solve the &lt;strong&gt;multi platform and scalability problems&lt;/strong&gt; (along with of course actually creating a product that fulfills a need or desire) &lt;strong&gt;will get bigger faster&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The opportunities are growing exponentially (network effect) for the talented, hard working teams that can execute.&lt;/p&gt;</content><category term="it"/><category term="prediction"/><category term="business model"/></entry><entry><title>Amazon S3 Bucket HTML Redirect</title><link href="https://blog.john-pfeiffer.com/amazon-s3-bucket-html-redirect/" rel="alternate"/><published>2012-04-13T23:57:00-07:00</published><updated>2012-04-13T23:57:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2012-04-13:/amazon-s3-bucket-html-redirect/</id><summary type="html">
&lt;p&gt;Goal: More efficient and less error prone method to update a regularly changed downloadable file&lt;/p&gt;
&lt;p&gt;Web page redirects enable you to change the URL of a web page on your S3 hosted website (e.g., from www.example.com/oldpage to www.example.com/newpage) without breaking links pointing to …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Goal: More efficient and less error prone method to update a regularly changed downloadable file&lt;/p&gt;
&lt;p&gt;Web page redirects enable you to change the URL of a web page on your S3 hosted website (e.g., from www.example.com/oldpage to www.example.com/newpage) without breaking links pointing to the old URL. Users accessing the old URL will automatically be redirected to the new one.&lt;/p&gt;
&lt;h3 id="redirect-a-single-object"&gt;Redirect a single object&lt;/h3&gt;
&lt;p&gt;Amazon updated their functionality to allow per object meta data based redirects:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;WebUI&lt;/li&gt;
&lt;li&gt;Object properties&lt;/li&gt;
&lt;li&gt;Metadata&lt;/li&gt;
&lt;li&gt;Add Website Redirect Location&lt;/li&gt;
&lt;li&gt;either /newpage.html (internal redirect) or http://example2.com/page.html (external redirect)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Or PUT the object (or a zero byte file) with the x-amz-website-redirect-location header set (i.e. http://example2.com/page.html)&lt;/p&gt;
&lt;p&gt;Or use the universal static html redirect method:&lt;/p&gt;
&lt;p&gt;So upload the following download.html that includes&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"refresh"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0; url=http://example.com/file-v2.tar.gz"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Whenever you have a new version of your file you only have to upload a new "download.html" with an updated meta refresh header and any Users and links will always download the newest version of your file.&lt;/p&gt;
&lt;p&gt;Note, javascript may help you open the download and then return to the original page but have strange interactions for a .txt file...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"text/javascript"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://example.com/file-v2.tar.gz"&lt;/span&gt;
&lt;span class="c1"&gt;//--&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="redirect-a-domain"&gt;Redirect a domain&lt;/h3&gt;
&lt;p&gt;With two domains, example1.com and example2.com:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an s3 bucket for example1.com (static web hosting)&lt;/li&gt;
&lt;li&gt;Set bucket property in the "Static Web Hosting" section, select "Redirect all requests to another host name" to redirect to example2.com&lt;/li&gt;
&lt;li&gt;Configure Route53 (AWS DNS) for example1.com to have an A record that has an Alias Target as an S3 Website Endpoint (the bucket from step 1)&lt;/li&gt;
&lt;li&gt;Register example1.com to use the Amazon name servers (from Route53)&lt;/li&gt;
&lt;li&gt;Add any further subdomain redirects (e.g. www.example1.com) by repeating steps 1 and 2&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="more-info"&gt;more info&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/how-to-page-redirect.html"&gt;http://docs.aws.amazon.com/AmazonS3/latest/dev/how-to-page-redirect.html&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="Amazon"/><category term="S3"/><category term="html redirect"/></entry><entry><title>LDAP and LDAPS with Apache Directory Studio and the Java Keystore</title><link href="https://blog.john-pfeiffer.com/ldap-and-ldaps-with-apache-directory-studio-and-the-java-keystore/" rel="alternate"/><published>2012-03-29T21:44:51-07:00</published><updated>2012-03-29T21:44:51-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2012-03-29:/ldap-and-ldaps-with-apache-directory-studio-and-the-java-keystore/</id><summary type="html">&lt;p&gt;The popularity of LDAP for directory service/lookup means Apache Directory Services + Apache Directory Studio is an excellent combination for getting started with identity and user management.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://directory.apache.org/apacheds/"&gt;http://directory.apache.org/apacheds/&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;on windows it requires a 32bit JRE =(&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Download then install: &lt;code&gt;dpkg -i  apacheds-1.5.7.deb&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;later on …&lt;/p&gt;&lt;/blockquote&gt;</summary><content type="html">&lt;p&gt;The popularity of LDAP for directory service/lookup means Apache Directory Services + Apache Directory Studio is an excellent combination for getting started with identity and user management.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://directory.apache.org/apacheds/"&gt;http://directory.apache.org/apacheds/&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;on windows it requires a 32bit JRE =(&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Download then install: &lt;code&gt;dpkg -i  apacheds-1.5.7.deb&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;later on uinstall is possible via: &lt;code&gt;dpkg -r apacheds&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;By default Apache DS listens on &lt;strong&gt;10389&lt;/strong&gt; and &lt;strong&gt;10636&lt;/strong&gt; (SSL)&lt;/p&gt;
&lt;p&gt;You can connect to it with Apache Dir Studio with "LDAP -&amp;gt; New Connection -&amp;gt; hostname , port = 10389"&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;BindDN or user (click on Check Authentication)
uid=admin,ou=system
password= secret

ldapsearch -v -H ldap://ldap.domain.com:10389 -x -D &amp;quot;uid=admin,ou=system&amp;quot; -w &amp;quot;secret&amp;quot; &amp;quot;(objectclass=*)&amp;quot; -b &amp;quot;ou=system&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="self-signed-java-keystore"&gt;Self Signed Java Keystore&lt;/h3&gt;
&lt;p&gt;A Java Keystore is different from a normal cert + key combo. Example "self signed" java keystore, key, certificate combo&lt;/p&gt;
&lt;p&gt;NOTE: CN should really be ldap.domain.com , not Zanzibar!!!!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;genkey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keyalg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;RSA&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;cn=zanzibar, ou=ApacheDS, o=ASF, c=US&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;validity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;730&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Press Enter to use the same password for certificate as we already entered above for the keystore&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storetype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="apacheds-config-file"&gt;ApacheDS Config File&lt;/h3&gt;
&lt;p&gt;SSL is enabled by default but needs to be modified to use a separate certificate:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo vi /var/lib/apacheds-1.5.7/default/conf/server.xml&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;ldapServer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ldapServer&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;allowAnonyn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;mousAccess=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;false&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;saslHost=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ldap.example.com&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;saslPrincipal=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ldap/ldap.example.com@EXAMPLE.COM&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;searchBaseDn=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ou=users,ou=system&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;maxTimeLimit=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;15000&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;maxSizeLimit=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1000&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="na"&gt;keystoreFile=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/var/lib/ssl/zanzibar.jks&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="na"&gt;certificatePassword=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;secret&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;transports&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;tcpTransport&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;address=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.0.0.0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;port=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;10389&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;nbThreads=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;8&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;backLog=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;50&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;enableSSL=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;false&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;tcpTransport&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;address=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.0.0.0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;port=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;10636&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;nbThreads=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;8&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;backLog=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;50&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;enableSSL=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/transports&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;hr&gt;
&lt;h3 id="start-and-verify-the-new-config"&gt;Start and verify the new config&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/etc/init.d/apacheds-1.5.7-default restart
/etc/init.d/apacheds-1.5.7-default status

netstat -an --inet
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;start the service and check ports listening, established, time wait (especially 10636)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;JXplorer or ApacheDirectoryStudio connection "LDAP -&amp;gt; New Connection -&amp;gt; hostname , port = 10636" (SSL or LDAPS)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;BindDN or user (click on Check Authentication)
uid=admin,ou=system 
password= secret
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;SELF SIGNED CERTIFICATE WARNING - you can view the certificate and choose to manually accept...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;NOTE IF YOU SEE THE ERROR "The connection failed - Connection refused: connect (zanzibar:10636)"&lt;br&gt;
    javax.naming.CommunicationException: zanzibar:10636 [Root exception is java.net.ConnectException: Connection refused: connect]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ENSURE THAT YOUR TRANSPORT IS NOT ONLY ALLOWING localhost! (below is the locked down setting...)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tcpTransport&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;10636&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;enableSSL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="modify-the-iptables-firewall"&gt;Modify the iptables firewall&lt;/h3&gt;
&lt;p&gt;Allow well known LDAP (389) and LDAPS (636) ports to work with ApacheDS&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 389 -j REDIRECT --to-port 10389
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 636 -j REDIRECT --to-port 10636
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="test-and-verify-connectivity-from-a-remote-machine"&gt;Test and verify connectivity from a remote machine&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ldapsearch -H ldap://ldap.domain.com:389 -x -D &amp;quot;uid=example,ou=system&amp;quot; -W -b &amp;quot;ou=system&amp;quot; &amp;quot;(uid=example)&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="extract-a-certificate-from-the-java-key-store"&gt;Extract a Certificate from the Java Key Store&lt;/h3&gt;
&lt;p&gt;EXTRACT A CERTIFICATE FROM A JKS INTO A .DER, THEN CONVERT IT INTO A .PEM AND IMPORT INTO THE JVM cacerts
(this allows a java based app to connect to your self signed cert LDAP server)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storetype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;

&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;zanzibar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;der&lt;/span&gt;
&lt;span class="n"&gt;openssl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x509&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;der&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;inform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;outform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PEM&lt;/span&gt;
&lt;span class="n"&gt;openssl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x509&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;

&lt;span class="n"&gt;openssl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s_client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10636&lt;/span&gt;&lt;span class="w"&gt;                           &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;Verify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;signed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;certificate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;openssl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s_client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10636&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;CAfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;Verify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;trustcacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;zanzibar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sun&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;changeit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="test-and-verify-the-ldaps-connection"&gt;Test and Verify the LDAPS connection&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl s_client -connect ldap.domain.com:636
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Verify return code: 18 (self signed certificate)
    openssl s_client -connect ldap.domain.com:636 -CAfile zanzibar.crt
Verify return code: 0 (ok)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;hr&gt;
&lt;h3 id="use-an-existing-certificate-and-key-and-create-a-java-key-store"&gt;Use an existing certificate and key and create a Java Key Store&lt;/h3&gt;
&lt;p&gt;EXISTING CERTIFICATE AND KEY AND CREATE A .JKS&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;openssl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkcs12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;inkey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;serveraliashostname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;CAfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;intermediatebundle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;caname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;
&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;

&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storetype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkcs12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id="convert-the-pkcs12-to-jks-format"&gt;CONVERT THE PKCS12 TO JKS FORMAT&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;importkeystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;srckeystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;srcstoretype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PKCS12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;deststoretype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JKS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;destkeystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jks&lt;/span&gt;

&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storetype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jks&lt;/span&gt;

&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;apacheds&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;keystoreFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/var/lib/ssl/keystore.jks&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;still need to figure out how to include the intermediate CAcert root chain...!&lt;/p&gt;
&lt;h3 id="importingreplacing-a-jks-keystore"&gt;IMPORTING/REPLACING A JKS KEYSTORE&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;importkeystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;deststorepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;destkeypass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;destkeystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;srckeystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;srcstoretype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PKCS12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;srcstorepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;oldaliasname&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;GUI CONVERTER FROM PKCS12 to JKS (requires Java 1.6 or higher)
&lt;a href="http://portecle.sourceforge.net/"&gt;http://portecle.sourceforge.net/&lt;/a&gt;  (open the .p12 , Tools -&amp;gt; Keystore type to JKS)
(seems to have a funny message where it needed change the password to "password)")&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;trustcacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ldap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;storepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;
&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;trustcacerts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GeoTrust&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;zanzibar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Both self signed and third party certificates can be supported with ApacheDS LDAPS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;LDAP with SSL is a little tricky and it's useful to use openssl and ldapsearch to verify.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;ldapServer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ldapServer&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;allowAnonymousAccess=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;false&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;saslHost=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ldap.example.com&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;saslPrincipal=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ldap/ldap.example.com@EXAMPLE.COM&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;searchBaseDn=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ou=users,ou=system&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;maxTimeLimit=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;15000&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;maxSizeLimit=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1000&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;keystoreFile=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/var/lib/ssl/zanzibar.jks&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;certificatePassword=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;secret&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;transports&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;tcpTransport&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;address=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.0.0.0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;port=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;10389&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;nbThreads=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;8&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;backLog=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;50&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;enableSSL=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;false&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;tcpTransport&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;address=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.0.0.0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;port=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;10636&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;nbThreads=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;8&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;backLog=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;50&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;enableSSL=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/transports&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;openssl s_client -connect domain.com:10636 -CAfile intermediate.crt&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ldapsearch -H ldaps://ldap.domain.com:10636 -x -D &amp;quot;uid=example,ou=system&amp;quot; -w PASSWORD -b &amp;quot;ou=system&amp;quot; &amp;quot;(uid=example)&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;ERROR: ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ON A REMOTE MACHINE IN ORDER TO USE LDAPSEARCH WITH LDAPS 636...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nano&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ldap&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ldap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;

&lt;span class="n"&gt;TLS_CACERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;intermediate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;
&lt;span class="n"&gt;TLS_CACERTDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="n"&gt;TLS_CERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;NOW THE BELOW WILL WORK&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ldapsearch -H ldaps://ldap.domain.com:10636 -x -D &amp;quot;uid=example,ou=system&amp;quot; -w PASSWORD -b &amp;quot;ou=system&amp;quot; &amp;quot;(uid=example)&amp;quot;
ldapsearch -H ldaps://ldap.domain.com:636 -x -D &amp;quot;uid=example,ou=system&amp;quot; -w PASSWORD -b &amp;quot;ou=system&amp;quot; &amp;quot;(objectclass=*)&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="it"/><category term="linux"/><category term="ldap"/><category term="apache"/><category term="directory"/><category term="studio"/><category term="ldaps"/><category term="ldapsearch"/><category term="java keystore"/><category term="keystore"/><category term="keytool"/></entry><entry><title>Self configuration tests for scalability</title><link href="https://blog.john-pfeiffer.com/self-configuration-tests-for-scalability/" rel="alternate"/><published>2012-03-12T02:42:00-07:00</published><updated>2012-03-12T02:42:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2012-03-12:/self-configuration-tests-for-scalability/</id><summary type="html">
&lt;p&gt;Goal: Customers (Users = system admins) able to self verify configuration.&lt;/p&gt;
&lt;p&gt;The more a user can self verify with software, the less support required per customer deployment.&lt;/p&gt;
&lt;p&gt;I used the Java API to quickly prototype a solution and exported a runnable .jar file...&lt;/p&gt;
&lt;p&gt;&lt;a href="https://code.google.com/p/atmos-java/"&gt;https://code.google.com/p/atmos-java/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="java-source-code-for-a-configuration-validation"&gt;Java source …&lt;/h3&gt;</summary><content type="html">
&lt;p&gt;Goal: Customers (Users = system admins) able to self verify configuration.&lt;/p&gt;
&lt;p&gt;The more a user can self verify with software, the less support required per customer deployment.&lt;/p&gt;
&lt;p&gt;I used the Java API to quickly prototype a solution and exported a runnable .jar file...&lt;/p&gt;
&lt;p&gt;&lt;a href="https://code.google.com/p/atmos-java/"&gt;https://code.google.com/p/atmos-java/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="java-source-code-for-a-configuration-validation"&gt;Java source code for a configuration validation&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.emc.esu.api.EsuApi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.emc.esu.api.rest.EsuRestApi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.emc.esu.api.EsuException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.emc.esu.api.ObjectId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.emc.esu.api.ObjectInfo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;org.apache.log4j.Level&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;org.apache.log4j.Logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;org.apache.log4j.ConsoleAppender&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;org.apache.log4j.PatternLayout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AtmosConnect&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rootLogger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRootLogger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;rootLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAllAppenders&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;hasMoreElements&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;rootLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;rootLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAppender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConsoleAppender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PatternLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PatternLayout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TTCC_CONVERSION_PATTERN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;rootLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Entering application"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;" does not equal the 4 required arguments."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"version 0.1: java -jar AtmosConnect.jar HOST PORT SUBTENANTID/UID SECRETKEY"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FULLTOKENID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SECRETKEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;displayConnectionCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FULLTOKENID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SECRETKEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;EsuApi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myEsuAPI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;myEsuAPI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EsuRestApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FULLTOKENID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SECRETKEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EsuException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"EsuRestApi Constructor failed."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;createAtmosObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myEsuAPI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;displayAtmosObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myEsuAPI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;deleteAtmosObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myEsuAPI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;rootLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Application Successful"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// end main()&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;displayConnectionCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FULLTOKENID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SECRETKEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Connecting to Host: "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Connecting on Port: "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Full Token ID: "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FULLTOKENID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Secret Key: **************"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//System.out.println( "Secret Key: " + SECRETKEY );&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;createAtmosObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EsuApi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myEsuAPI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myEsuAPI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"application/octet-stream"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Created object: "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Create Object failed."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;//e.printStackTrace();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// JUnit Tests: Invalid Host, Port, SubtenantID, UID, Shared Secret, etc.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;displayAtmosObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EsuApi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myEsuAPI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;ObjectInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;myObjectInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myEsuAPI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getObjectInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//System.out.println( "ObjectInfo: " + myObjectInfo.toString() );&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//System.out.println( "ObjectInfo as XML: " + myObjectInfo.getRawXml() );&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;deleteAtmosObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EsuApi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myEsuAPI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Trying to delete Server Object: "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;myEsuAPI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Test Object deleted on Server: "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Delete Object "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myObjectId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;" failed "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;//e.printStackTrace();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// end class&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr/&gt;
&lt;h3 id="verify-the-bash-commands"&gt;Verify the bash commands&lt;/h3&gt;
&lt;p&gt;Verify bash commands that will help extract the configuration:  &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'emcIpAddress='&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tomcat6&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;webapps&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;storagegateway&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;WEB&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="bp"&gt;INF&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'='&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="modify-the-perl-console-menu-script"&gt;Modify the perl console menu script&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stopped&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;rc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;RUNLEVEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;[&lt;span class="mi"&gt;2345&lt;/span&gt;]&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nv"&gt;stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;runlevel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;[&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="mi"&gt;2345&lt;/span&gt;]

&lt;span class="nv"&gt;respawn&lt;/span&gt;

&lt;span class="k"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;sbin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;getty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;init&lt;/span&gt;.&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;CONSOLEMENU&lt;/span&gt;.&lt;span class="nv"&gt;pl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;38400&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tty1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;sudo vi /etc/init.d/CONSOLEMENU.pl&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;sub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;testatmosconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"clear"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$sourcefilename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"/var/lib/tomcat6/webapps/storagegateway/WEB-INF/app.properties"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$applicationfilename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"/var/lib/tomcat6/oxygen-storagegateway/atmosconnect.jar"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$sourcefilename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$applicationfilename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$emcipaddress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;qx( /bin/grep -i 'emcIpAddress=' $sourcefilename | cut -f 2 -d '=' )&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;chomp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$emcipaddress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$emcportnumber&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;qx( /bin/grep -i 'emcPortNumber=' $sourcefilename | cut -f 2 -d '=' )&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;chomp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$emcportnumber&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$emcuid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;qx( /bin/grep -i 'emcUid=' $sourcefilename | cut -f 2 -d '=' )&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;chomp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$emcuid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$emcsharedsecret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;qx( /bin/grep -i 'emcSharedSecret=' $sourcefilename | cut -f 2 -d '=' )&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;chomp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$emcsharedsecret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;@status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;qx( /usr/bin/java -jar $applicationfilename $emcipaddress $emcportnumber $emcuid $emcsharedsecret )&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"@status\n"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"$sourcefilename or $applicationfilename does not exist.\n"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# end atmosconnect()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="goal-achieved"&gt;Goal Achieved&lt;/h3&gt;
&lt;p&gt;A user can self test if they've misconfigured the VM or there's  missing conf files/app, etc. SUCCESS!&lt;/p&gt;
&lt;p&gt;Next steps: further unit and system tests, debug, refactor&lt;/p&gt;</content><category term="programming"/><category term="java"/><category term="perl"/><category term="TDD"/></entry><entry><title>Technology Careers (part 1)</title><link href="https://blog.john-pfeiffer.com/technology-careers-part-1/" rel="alternate"/><published>2012-02-27T02:38:00-08:00</published><updated>2012-02-27T02:38:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2012-02-27:/technology-careers-part-1/</id><summary type="html">
&lt;h3 id="a-system-administrator-is-like-a-firefighter"&gt;A System Administrator is like a Firefighter&lt;/h3&gt;
&lt;p&gt;Always available to fix an emergency, but without the glamour; after putting out a fire a SysAdmin has to report (with vague implications of guilt): what happened, why it happened, and how it can be prevented in the future. (Who's fault is it …&lt;/p&gt;</summary><content type="html">
&lt;h3 id="a-system-administrator-is-like-a-firefighter"&gt;A System Administrator is like a Firefighter&lt;/h3&gt;
&lt;p&gt;Always available to fix an emergency, but without the glamour; after putting out a fire a SysAdmin has to report (with vague implications of guilt): what happened, why it happened, and how it can be prevented in the future. (Who's fault is it?)&lt;/p&gt;
&lt;p&gt;Good System Administrators are well paid but usually work on the invisible and complex "back end" and are on "on call".&lt;/p&gt;
&lt;p&gt;(Yes, your dinner, sleep, and other private activities will be interrupted.)&lt;/p&gt;
&lt;p&gt;A stable, in demand, career since it's hard to fire a good System Administrator but there's often a belief that the position could/should be outsourced.&lt;/p&gt;
&lt;p&gt;(Ever hear the story of the SysAdmin who was so good at automating server tasks and desktop deployments that he put himself out of a job?)&lt;/p&gt;
&lt;p&gt;The overly general term "Operations" is often applied.  In non technology focused companies you might be managed by a Chief Operations Officer who is more concerned with facilities (buildings), logistics (trucks, ships, planes, etc.) or even janitorial services.&lt;/p&gt;
&lt;p&gt;If you're not careful, methodical, detailed, and cool headed then this may not be the right path for you. (Cynical is optional but it helps a lot).&lt;/p&gt;
&lt;h3 id="an-it-manager-is-like-an-accountant"&gt;An IT Manager is like an Accountant&lt;/h3&gt;
&lt;p&gt;Always asked to do more with less. In fairness an IT Manager gets to be more creative (legally) as innovation and change drive technological efficiency.&lt;/p&gt;
&lt;p&gt;Good IT Managers spend as little time as possible in meetings but can organize and automate the heck out of everything. Diagrams, Business Cases, Reporting, Schedules; not often a hands on role.&lt;/p&gt;
&lt;p&gt;Being in charge of IT resources is well paid and it's a stable career (you're in charge of the budget, right?) but when "downsizing" occurs you may be considered an acceptable loss (if your business cases prove you've saved/earned enough then it's usually a non issue).&lt;/p&gt;
&lt;p&gt;Gluing together services often means jugging time zones (remote contractors and services are the norm) and balancing/network internal departmental demands.&lt;/p&gt;
&lt;p&gt;If you're not good at understanding people or technology (yes you have to hire/fire people and purchase wisely) then this may not be the right path for you.&lt;/p&gt;
&lt;h2 id="a-developer-is-like-an-artist"&gt;A Developer is like an Artist&lt;/h2&gt;
&lt;p&gt;Eccentric, misunderstood, and something rare for the Arts, well paid.&lt;/p&gt;
&lt;p&gt;Good Developers understand it's Art with a purpose and apply rigorous engineering principles to their work (since software runs things like airplanes and hospitals this is a good thing).&lt;/p&gt;
&lt;p&gt;It's well paid but sometimes unpredictable work (software project done = goodbye expensive labor), luckily there's an endless demand for the foreseeable future. &lt;/p&gt;
&lt;p&gt;(Interestingly it often takes awhile to discover a "bad" developer and even then employers are reluctant to let them go).&lt;/p&gt;
&lt;p&gt;If you're not creative and logical; unable to unriddle paradoxes (yes it's a required dependency but no we don't want it), or just don't want to spend most of your waking hours in front of a screen (human contact optional), then this might not be the right path for you.&lt;/p&gt;</content><category term="leadership"/><category term="career"/><category term="management"/></entry><entry><title>Test Driven Development Introduction and QA Domains</title><link href="https://blog.john-pfeiffer.com/test-driven-development-introduction-and-qa-domains/" rel="alternate"/><published>2012-01-20T17:44:00-08:00</published><updated>2012-01-20T17:44:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2012-01-20:/test-driven-development-introduction-and-qa-domains/</id><summary type="html">
&lt;h3 id="why-test-driven-development"&gt;Why Test Driven Development&lt;/h3&gt;
&lt;p&gt;"6% progress" in engineering&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Cost: Organizations want to reduce the time and cost associated with releasing code including the post release support, debugging, and maintenance.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Agility: TDD enables faster product reactions to market changes and more drastic product changes and continued extension of successful products&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Growth …&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">
&lt;h3 id="why-test-driven-development"&gt;Why Test Driven Development&lt;/h3&gt;
&lt;p&gt;"6% progress" in engineering&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Cost: Organizations want to reduce the time and cost associated with releasing code including the post release support, debugging, and maintenance.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Agility: TDD enables faster product reactions to market changes and more drastic product changes and continued extension of successful products&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Growth: it’s a proven method for Organizations building a strong brand and looking for 6% compound growth, enables larger teams and rotation of personnel&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Individual Time: less time is spent in long debugging session or writing "dead code" (code that never reaches production for either Business reasons – i.e. no customer demand, or Engineering decisions – i.e. quality of the overall product never reaches a release point).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Individual Careers: ability to work on more complex projects and clear contribution to business success&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Individual Satisfaction: confidence in what’s been delivered and happier users&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="acceptance-test-driven-development"&gt;Acceptance Test Driven Development&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Everyone agrees on done!&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Automated Tests create concrete artifacts for all stakeholders (i.e. PM, QA, Engineering, etc.) to understand scope, review progress, and agree on "done".   This vastly improves inefficiencies in communication, scheduling, and manual testing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Design is codified into a common language of Tests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Specifications require less translation and have faster validation&lt;/li&gt;
&lt;li&gt;Deliverables are more accurate to the business needs and progress is more accurate&lt;/li&gt;
&lt;li&gt;Future maintenance and extendibility is built into the process&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Acceptance Tests do require more up front discussions (not coding!) and setup time for automation.&lt;br/&gt;
&lt;em&gt;Good tests must match production environments and requirements in order to be valid.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Tests must be engineered well in order to give good results as they are a product too.
TDD does not guarantee good designs, good thinking, or good code.  &lt;strong&gt;It’s up to people to make it work!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;(Unit) Test Driven Development&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Simple, easy, lean, fast, readable, and early.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Testing should be easy so start small, ask questions, and get used to the world being upside down =)&lt;/li&gt;
&lt;li&gt;Tests must factor out Dependencies (helps design modularization and isolation)&lt;/li&gt;
&lt;li&gt;Tests must run fast (slow tests can be moved into Acceptance Testing)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Consider writing tests in order of expected probability of occurrence (balanced by severity if coverage is missed) i.e. the most common usage is correct inputs generating correct output, next maybe 2% of invalid inputs will generate 85% of data corruption&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;"Happy Path" verifies that the code will fulfill the functionality specifically asked for so these are the "high value" tests.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Next verify most likely invalid inputs and important exception handling&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The law of diminishing returns: more tests means more code writing and more tests that require maintenance&lt;/li&gt;
&lt;li&gt;Tests uncover assumptions, dependencies, tight coupling, and duplication.&lt;/li&gt;
&lt;li&gt;Every Developer must be able to read and maintain the tests; it is a shared effort at making a better product so clarity and readability are critical.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="tdd-workflow"&gt;TDD Workflow&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Acceptance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Acceptance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Passes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TDD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;please&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                         &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="n"&gt;AT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fails&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Passes&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Unit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Unit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Tests&lt;/span&gt;
&lt;span class="w"&gt;                                      &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;
&lt;span class="w"&gt;                             &lt;/span&gt;&lt;span class="n"&gt;UT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fails&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;                                        &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;                                  &lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="trivial-example-using-an-adding-positive-integers-only-calculator"&gt;Trivial Example using an "Adding Positive Integers Only Calculator"&lt;/h4&gt;
&lt;p&gt;add( int a , int b)
- testPositiveIntegerPlusPositiveInteger ("happy path")
- testIntegerMinusPositiveInteger (invalid input)
- testZeroPlusPositiveInteger (invalid input)
- testStringPlusInteger (edge case invalid input: maybe the compiler does not catch this)&lt;/p&gt;
&lt;h4 id="test-doubles-using-stubs-or-mocks-factoring-out-dependencies"&gt;"Test Doubles" using Stubs or Mocks (Factoring out dependencies)&lt;/h4&gt;
&lt;p&gt;Using stubs or mocks we can focus testing only the code we’ve written, Stubs generally are simple hard coded ways to validate state.&lt;/p&gt;
&lt;h3 id="source-code-examples-of-tdd"&gt;Source code examples of TDD&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;dependencyLibrary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;adder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dependencyLibraryStub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dependencyLibrary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;dependencyLibraryStub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expectedResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Override&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;adder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;expectedResult&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Mocks are usually leveraged with a framework and can validate process, multiple interactions, as well as state.&lt;/p&gt;
&lt;h3 id="qa-domains"&gt;QA Domains&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Start Testing with the most used paths and most user visible areas.&lt;/li&gt;
&lt;li&gt;Find the most serious consequence -&amp;gt; force error handling or crash.&lt;/li&gt;
&lt;li&gt;Make it easy to test (invest in GUI / scripting, don't slow testing down by requiring careful command line typing)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Automate testing (especially regression testing).&lt;/p&gt;
&lt;p&gt;functionality
communication / documentation
command structure
performance / load
output (including error messages)
compatible softare / hardware
stack overflow
garbage collection&lt;/p&gt;
&lt;p&gt;errors - boundary
        -math / time
        -startup
        -long running
        -pause &amp;amp; restart / resume
        -backup &amp;amp; restore
        -different data back &amp;amp; forth
        -race conditions
        -denial of resources&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="dependencies"&gt;Dependencies&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;RAM full, Hard Drive full scenarios, cpu full, network slow
locked files (e.g. OS is using)
Error accessing media (slow disk, bad network, etc.)
Other Shared Resource locks?
Special Modes (i.e. airplane/offline mode, disconnected peripheral, etc.

Remove/Rename files and folders the app depends on
  Corrupt one of the above
  hidden file? permissions changed...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="file-systems-language-and-text"&gt;File Systems, Language, and Text&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Ascii vs UTF8&lt;/li&gt;
&lt;li&gt;File size 0, negative? and very large&lt;/li&gt;
&lt;li&gt;Many small files, many large files&lt;/li&gt;
&lt;li&gt;Directory or Folder instead of File and vice versa&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Symbolic links and shortcuts&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Invalid paths (OS do the checks!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Max path length&lt;/li&gt;
&lt;li&gt;longest file name (symbols)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;reserved file names?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;try pasting (rather than typing)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;special control chars \n  &amp;lt;a href&lt;br/&gt;
&lt;code&gt;-1234567890-1234567890--1234567890--1234567890&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Escape sequences
    &lt;code&gt;‘\’. ‘\’ = %5C = %255C = %%35%63 = %25%35%63 )&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HTML Encoding check where applicable: &lt;code&gt;‘&amp;lt;’ = &amp;lt; = &amp;amp;#x3C; = &amp;amp;60&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;!  #   $&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="byte-boundaries"&gt;byte boundaries&lt;/h5&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;535&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;147&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;483&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;648&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;147&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;483&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;647&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="standard-test-cases"&gt;Standard Test Cases&lt;/h4&gt;
&lt;h4 id="viewing"&gt;Viewing&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Resolution 1024 x 768 but also try 800x600 and 1280 and strange ones&lt;/li&gt;
&lt;li&gt;Resizing? (all the way down and back up)&lt;/li&gt;
&lt;li&gt;Minimizing and backgrounding&lt;/li&gt;
&lt;li&gt;Excessive Requests&lt;/li&gt;
&lt;li&gt;performance (too slow might be unusable)&lt;/li&gt;
&lt;li&gt;Single item too long horizontal&lt;/li&gt;
&lt;li&gt;Single item too long vertical&lt;/li&gt;
&lt;li&gt;Too many items returned horizontal &lt;/li&gt;
&lt;li&gt;Too many items returned vertical&lt;/li&gt;
&lt;li&gt;Dropdown &lt;/li&gt;
&lt;li&gt;scrollbar = logical / physical batches of results?&lt;/li&gt;
&lt;li&gt;Web Page&lt;/li&gt;
&lt;li&gt;chrome, firefox(2,3), ie (6,7,8,9), safari (i.e. without SSO), opera?&lt;/li&gt;
&lt;li&gt;able to bookmark?&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="user-input"&gt;User Input&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Text Entry&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;too few (blank) and too many chars&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;funny chars (UTF8? and symbols &lt;code&gt;!@#$%&amp;amp;^*()[]\|;':",./&amp;lt;&amp;gt;?&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Special case = Email Address or Phone Number?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Excessive Submits&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Login &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;empty username and or empty password&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;wrong username&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;wrong password&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;already logged in&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;username does not exist?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;user does not have permission?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;back button&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Search (similar to above but also includes)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;case sensitive? (user notified?)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;wild card&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;no data set exists to search (e.g. user search but no users?)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;permissions - should be able to see these results?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="upload-a-file"&gt;Upload a file&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;UTF8 file names, very long names, very short names, special characters (. / ? &amp;lt; &amp;gt;)&lt;/li&gt;
&lt;li&gt;case sensitivity, reserved key names&lt;/li&gt;
&lt;li&gt;upload normal expected extensions (jpg, gif, png etc.)&lt;/li&gt;
&lt;li&gt;try random or restricted extensions (.exe, .bat, no extension, super long chars, special chars)&lt;/li&gt;
&lt;li&gt;tiny file or really large file&lt;/li&gt;
&lt;li&gt;0 byte file&lt;/li&gt;
&lt;li&gt;compressed (zip, 7z, rar, etc.)&lt;/li&gt;
&lt;li&gt;interrupt the upload (does it fail cleanly? resume?)&lt;/li&gt;
&lt;li&gt;race condition of two different sessions uploading the same file name (different content)&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="whitebox-glassbox-looking-in-the-code"&gt;Whitebox "Glassbox" Looking in the code&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;InvalidSession Exception&lt;/li&gt;
&lt;li&gt;Offline Exception, &lt;/li&gt;
&lt;li&gt;No Permission Exception&lt;/li&gt;
&lt;li&gt;Could not retrieve from source Exception&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unexpected Exception&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;array boundary&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;memory leaks / free&lt;/li&gt;
&lt;li&gt;race conditions / locked&lt;/li&gt;
&lt;li&gt;global variables (creates a hidden race condition or lock)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="error-messages"&gt;Error Messages&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Do they appear when there's a problem?&lt;/li&gt;
&lt;li&gt;Are they accurate?&lt;/li&gt;
&lt;li&gt;Can they be understood?&lt;/li&gt;
&lt;li&gt;Do they direct the user on how to the correct the problem?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What to log or print in an error&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="n"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cannot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;"action user tried"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Reason&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;why&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;throwing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="ss"&gt;"MyComponent Driver Error"&lt;/span&gt;
&lt;span class="n"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cannot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;"user@domain.com"&lt;/span&gt;
&lt;span class="n"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;write&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;permission&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="for-your-consideration"&gt;For your consideration&lt;/h3&gt;
&lt;h4 id="stress"&gt;Stress&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;"thundering herd" and exponential backoff&lt;/li&gt;
&lt;li&gt;cpu processing, data sizes, input volumes, frequency&lt;/li&gt;
&lt;li&gt;what breaks when it fails&lt;/li&gt;
&lt;li&gt;can it be disabled&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="time"&gt;Time&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;concurrency&lt;/li&gt;
&lt;li&gt;stale/null/indeterminate state&lt;/li&gt;
&lt;li&gt;archived/deleted&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="operations"&gt;Operations&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;upgrades, migrations, backups/restore&lt;/li&gt;
&lt;li&gt;UX: new users and new features&lt;/li&gt;
&lt;li&gt;power users vs newbs vs admins, international, 3rd party devs, support&lt;/li&gt;
&lt;li&gt;logging, metrics, and analytics&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="platform"&gt;Platform&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;variability in OS, browser, screens, devices, databases, connections&lt;/li&gt;
&lt;li&gt;external dependency handling?&lt;/li&gt;
&lt;li&gt;licensing and tier thresholds (i.e. group storage limit)&lt;/li&gt;
&lt;li&gt;SaaS vs cluster vs on premise considerations&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="usability"&gt;Usability&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;consistency&lt;/li&gt;
&lt;li&gt;empty state, keyboard vs mouse vs ?&lt;/li&gt;
&lt;li&gt;customization? (saved preferences)&lt;/li&gt;
&lt;li&gt;what's the learning curve? too little/too much documentation?&lt;/li&gt;
&lt;li&gt;list UI: sorting, ordering, &lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="security"&gt;Security&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Access and Audit logs&lt;/li&gt;
&lt;li&gt;who should be able to access via Web UI, API, etc.&lt;/li&gt;
&lt;li&gt;XSS, XSRF&lt;/li&gt;
&lt;li&gt;where's the edge? tiered service layers (clearly defined boundaries for authorization)&lt;/li&gt;
&lt;li&gt;leaking information (what can anonymous or normal users see they should not)&lt;/li&gt;
&lt;li&gt;logging password/sensitive info&lt;/li&gt;
&lt;/ul&gt;</content><category term="programming"/><category term="tests"/><category term="tdd"/><category term="qa"/><category term="edge cases"/><category term="testing"/></entry><entry><title>Yes, Change your Password regularly</title><link href="https://blog.john-pfeiffer.com/yes-change-your-password-regularly/" rel="alternate"/><published>2011-12-27T17:10:00-08:00</published><updated>2011-12-27T17:10:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2011-12-27:/yes-change-your-password-regularly/</id><summary type="html">
&lt;h3 id="security-is-risk-management"&gt;Security is risk management&lt;/h3&gt;
&lt;p&gt;The hundreds of articles about "changing passwords doesn't improve security" are just hype/noise without context.&lt;/p&gt;
&lt;p&gt;There is an absolute spectrum of password management that creates untenable risk (no password or simply the word "password") all the way to very low risk (100 random characters of …&lt;/p&gt;</summary><content type="html">
&lt;h3 id="security-is-risk-management"&gt;Security is risk management&lt;/h3&gt;
&lt;p&gt;The hundreds of articles about "changing passwords doesn't improve security" are just hype/noise without context.&lt;/p&gt;
&lt;p&gt;There is an absolute spectrum of password management that creates untenable risk (no password or simply the word "password") all the way to very low risk (100 random characters of every category changed every day) but at an unbearable overhead.&lt;/p&gt;
&lt;p&gt;While "changing the highly complex password every 90 days" is considered inefficient and draconian...&lt;/p&gt;
&lt;h3 id="what-about-at-least-changing-the-password-once-a-year"&gt;What about at least changing the password once a year?&lt;/h3&gt;
&lt;p&gt;While it's likely that an external attacker or disgruntled employee will use a compromised password immediately, it doesn't mean there isn't a good reason to choose a frequency of password change:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If a sticky note attached to a laptop sold at a garage sale is still valid for the company's online bank account then you're in trouble.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Reduce the risk, have a policy to manage that window of access to something you're comfortable with.&lt;/p&gt;
&lt;h3 id="dependency-visibility"&gt;Dependency Visibility&lt;/h3&gt;
&lt;p&gt;The opportunity to improve your infrastructure is well worth the cost of finding every hard coded place that a password is embedded in your organization:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The password was just changed&lt;/li&gt;
&lt;li&gt;You quickly find something is not working&lt;/li&gt;
&lt;li&gt;You either change the password back or refactor/reorganized and break the dependency&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Far better than:&lt;/strong&gt;
- a random event like a forgotten password reset by one individual in the organization
- creates a mystery problem in mission critical systems for everyone else to track down&lt;/p&gt;
&lt;h3 id="but-i-use-a-password-management-tool"&gt;But I use a password management tool!&lt;/h3&gt;
&lt;p&gt;Password management tools have to store their passwords somewhere (hopefully encrypted).  &lt;/p&gt;
&lt;p&gt;Over time there is a chance that a "bad actor" will end up with your local password store.&lt;/p&gt;
&lt;p&gt;Man in the middle attacks (NSA anyone?) can snatch credentials from secure channels.&lt;/p&gt;
&lt;p&gt;There is an even higher likelihood that the remote services you are using will be compromised (database or password hashes leak).&lt;/p&gt;
&lt;p&gt;Given time hackers can use rainbow tables and GPU based brute force attacks they will crack your password.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Every time you change your password you reset the clock on every type of attack&lt;/strong&gt;&lt;/p&gt;</content><category term="it"/><category term="security"/><category term="password"/></entry><entry><title>Using find grep cut awk sort dd with files and text and listing files with ls</title><link href="https://blog.john-pfeiffer.com/using-find-grep-cut-awk-sort-dd-with-files-and-text-and-listing-files-with-ls/" rel="alternate"/><published>2011-11-12T02:37:00-08:00</published><updated>2011-11-12T02:37:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2011-11-12:/using-find-grep-cut-awk-sort-dd-with-files-and-text-and-listing-files-with-ls/</id><summary type="html">
&lt;p&gt;There are amazing linux command line utilities that make finding and manpiulating files very easy&lt;/p&gt;
&lt;h2 id="create-copy-truncate"&gt;create copy truncate&lt;/h2&gt;
&lt;p&gt;Assuming you understand the basics like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;touch example.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;make an empty file named file.txt&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;echo "hi" &amp;gt;&amp;gt; example.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;append the hi content into example.txt, note that a single &amp;gt; will …&lt;/p&gt;&lt;/blockquote&gt;</summary><content type="html">
&lt;p&gt;There are amazing linux command line utilities that make finding and manpiulating files very easy&lt;/p&gt;
&lt;h2 id="create-copy-truncate"&gt;create copy truncate&lt;/h2&gt;
&lt;p&gt;Assuming you understand the basics like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;touch example.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;make an empty file named file.txt&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;echo "hi" &amp;gt;&amp;gt; example.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;append the hi content into example.txt, note that a single &amp;gt; will overwrite the contents&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat example.txt | tee -a example2.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;display the contents of the file to the output console and also pipe the result to the &lt;strong&gt;tee&lt;/strong&gt; utility which appends it to another file - tee is better than &amp;gt;&amp;gt; &lt;/li&gt;
&lt;li&gt;except that tee always returns 0 so if you have set -e then prefer &amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;:&amp;gt; example.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;truncate the file without disturbing any existing readers of that file (e.g. zero a log without messing up an applications ability to write to the file)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cp -a example.txt example2.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;copy archive which preserves timestamp (but will also overwrite the target - in this example probably zero bytes)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;rm example.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;remove a file, -f forces removal without a prompt, -rf to recursively force remove a file or directory &lt;strong&gt;be careful&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="ls-lists-files-in-a-directory"&gt;ls lists files in a directory&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls /etc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;list the files and directories in the /etc directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;lists just the names (not including hidden .dot files or directories) in the current directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -1a
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;lists just the names including hidden .dot files&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -ahl | grep -v IGNORETHIS
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;exclude lines that match IGNORETHIS&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -f
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;unsorted list of files which is the only way to work with directories with a very large number of files&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="list-directories"&gt;list directories&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -d */
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;list names of directories in the current directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -1 -d */
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;using a "numeral one" parameter lists one directory name per line&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -l -d */
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;using a "lowercase letter L" parameter lists the extended information (permissions, owner, timestamps)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -d ./*/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;list names of directories in the current directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;echo */
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;lists the names of directories in the current directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find recursively starting from the current directory and display the relative path all objects of type directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;maxdepth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find all directories in /tmp up to 1 level deep (/tmp/foo)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;maxdepth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find all directories in /tmp up to 2 levels deep (/tmp/foo/bar)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="counting-the-listings"&gt;counting the listings&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -1 | wc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;lists just the names of non hidden files piped to word count, number one looks a lot like lowercase L (sadindeed)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -1 | wc -l
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;list just names and count only the lines (number one, then lowercase L)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -ahl | wc -l
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;counting hidden files BUT directories too&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;for file in /PATH/foo*; do cp "$file" /mnt/BAR/; done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;when there are too many files in a directory: "/bin/cp: Argument list too long"
a one liner for copying each file as a parameter&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="sorting-by-size-or-timestamp"&gt;sorting by size or timestamp&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -Sla
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;sort by size (largest to smallest with full details displayed, show . hidden files too)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -Slar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;sort by size (reversed, so smallest to largest with full details displayed, show . hidden files too)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -Slarh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;sort by size (reversed, so smallest to largest with full details displayed, show . hidden files too, human sizes like MB)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -ahtlr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;sort by timestamp (reversed so oldest first, show . hidden files too, human sizes like MB)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -ahtlr | head -n3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;only the 3 oldest lines&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls -la --time-style=full-iso foobar.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;list the full modified timestamp
&lt;a href="https://www.gnu.org/software/coreutils/manual/html_node/Formatting-file-timestamps.html"&gt;https://www.gnu.org/software/coreutils/manual/html_node/Formatting-file-timestamps.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="find"&gt;find&lt;/h2&gt;
&lt;p&gt;find is better than locate because locate depends on a cron job to index the file system and so may miss recent results&lt;/p&gt;
&lt;h3 id="find-to-list-files"&gt;find to list files&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find and display the relative path all objects of type file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;maxdepth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;wc = count lines = files in the /tmp directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;to get all subdirs too&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="find-a-specific-file-or-types-of-files"&gt;find a specific file or types of files&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find -name "MyCProgram.c"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;case sensitive, starts in the current directory &lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find startdirectory -name 'partoffileordirname'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;e.g. find /home/joe -name '.tx'
which would return txt's as well as txv?'s&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find / -iname "MyCProgram.c"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;case insensitive, starts from root &lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find -maxdepth 1 -not -iname "MyCProgram.c"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;case insensitive, starts from current directory, will search subdirectory(ies) and list all items //that do NOT match the query&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the largest 5 files&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;the smallest not empty 5 files &lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;all directories in the current directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;count the number of files (can recurse subdirectories)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;iname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pyc&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;PYC&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;move all .pyc files (start from this directory and indefinitely recurse down)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find . -name "*api*" -exec cat {} \;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find everything containing api and cat it&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;iname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yaml&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;needle&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;search the contents of a specific file extension and output the filename and line number of each match&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="find-and-exec-to-modify-a-set-of-files"&gt;find and exec to modify a set of files&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"*api*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;objectid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find all files that contain an api and output the contents but filter to only display lines that contain "objectid"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;maxdepth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;du&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;only one level down if it's a directory show the disk usage summary (human sizes)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DIR1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DIR2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;maxdepth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;basename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;uniq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;lists all file names in directories, sorted, show only repeats (aka duplicates)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;-name&lt;span class="w"&gt; &lt;/span&gt;'*.txt'&lt;span class="w"&gt; &lt;/span&gt;-exec&lt;span class="w"&gt; &lt;/span&gt;sh&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;'mv&lt;span class="w"&gt; &lt;/span&gt;"$0"&lt;span class="w"&gt; &lt;/span&gt;"&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;.java"'&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt; &lt;/span&gt;\;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find all .txt files and renames them to .java&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"$f"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"$(echo $f | sed 's/-/\ /g')"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;iname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;".py"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;py&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;py&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;only works on the current directory (no recursion?)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;-type&lt;span class="w"&gt; &lt;/span&gt;f&lt;span class="w"&gt; &lt;/span&gt;-iname&lt;span class="w"&gt; &lt;/span&gt;'.py'&lt;span class="w"&gt; &lt;/span&gt;|&lt;span class="w"&gt; &lt;/span&gt;while&lt;span class="w"&gt; &lt;/span&gt;read&lt;span class="w"&gt; &lt;/span&gt;filename;&lt;span class="w"&gt; &lt;/span&gt;do&lt;span class="w"&gt; &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;"&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;"&lt;span class="w"&gt; &lt;/span&gt;"echo&lt;span class="w"&gt; &lt;/span&gt;"&lt;span class="cp"&gt;${&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;"&lt;span class="w"&gt; &lt;/span&gt;|&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;'s/\.py$/\.py.txt/'";&lt;span class="w"&gt; &lt;/span&gt;done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;a lot of extra work to achieve a recursive rename from .py to .py.txt&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;iname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"*.java"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Hni&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"case-insensitive-text"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find java files and return if they contain some text&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;iname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"*.java"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Hn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"fileSizeInMB &amp;lt; 100"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find java files and return if they contain some case sensitive text&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;directoryname&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ahl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find case insensitive directories beginning with "directoryname" and list their contents&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;iname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".txt"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find case insensitive files ending with ".txt" and change their owner to root and group to  www-data&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;iname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*.txt"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chmod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find case insensitive files ending with ".txt" and change their permissions to 640&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;iname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chmod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;750&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find case insensitive directories beginning with "web" and change their permissions to 750&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;iname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"*.sh"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;";"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find files ending in .sh and move them into the current directory&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;mtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;540&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;mtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;720&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nx"&gt;AD&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;|%&lt;/span&gt;&lt;span class="nx"&gt;TD&lt;/span&gt;&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;csv&lt;/span&gt;

&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//check the home directory for empty files (size 0)&lt;/span&gt;


&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;mindepth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;maxdepth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;iname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;passwd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;case insensitive, starts from root, will search subdirectory levels between 2 and 4&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find / 3 -maxdepth 5 -iname passwd &amp;amp;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;case insensitive, starts from root, will search at most 4 subdir levels, will start in background 
note that you'll have to press enter once as the text results will scroll to interrupt your 
bash session ... once the job's done pressing enter will return you to the prompt&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;iname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MyCProgram.c"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;md5sum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt; &lt;/span&gt;\&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;interesting use: creating a md5sum of all of the results&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;inum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16187430&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;new&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;\&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;interesting - find a file by inode number (ls -i) and then rename/move it&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;perm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find all files from root below, with permissions set exactly to 700, only regular files (-type f)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;perm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;while the above just lists the files the below runs an ls -l to see everything about them...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;RUN "man find" IF YOU NEED TO FIND SOMETHING SPECIFIC ABOUT FILES AND PERMISSIONS&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;any files newer than the one given find -newer file-i-made-yesterday&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;search the home directory size equal to 100 MB, use +100MB for greater than and -100MB for less than find ~ -size 100M&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.thegeekstuff.com/2009/03/15-practical-linux-find-command-examples/"&gt;http://www.thegeekstuff.com/2009/03/15-practical-linux-find-command-examples/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="find-files-by-modified-time"&gt;find files by modified time&lt;/h3&gt;
&lt;p&gt;There is an implied AND operator with find but for OR or NOT...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find / -mmin -10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;something modified 10 minutes ago&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find . -mtime 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find files modified between 24 and 48 hours ago &lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find . -mtime +1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find files modified more than 48 hours ago&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find . -mmin +5 -mmin -10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find files modifed between # 6 and 9 minutes ago&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;mtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;xargs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;weekly_incremental&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tar&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find files modified in the last 7 days and create a .tar file from them&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find / -name core -delete
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;same if using Gnu find&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find / -user username
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;find all of the files a user owns..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;mtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;means&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;looking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;modified&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;days&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ago&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;mtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;means&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;less&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;than&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;days&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;mtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;skip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;means&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;exactly&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;days&lt;/span&gt;.

&lt;span class="nv"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;mtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;mtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;24 hours&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="grep"&gt;grep&lt;/h2&gt;
&lt;p&gt;grep is an amazing tool for getting efficiently finding text, &lt;a href="http://www.gnu.org/software/grep/manual/grep.html"&gt;http://www.gnu.org/software/grep/manual/grep.html&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="grep-parameters-and-examples-explained"&gt;grep parameters and examples explained&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat access.log | grep -v "bingbot"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;exclude from output lines that match bingbot&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;grep -r -i -w -n -A2 -B1 'hidden' /tmp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;search the /tmp directory and subdirectories recursively&lt;/li&gt;
&lt;li&gt;case insensitive&lt;/li&gt;
&lt;li&gt;only match the whole word, so "thidden" would not be returned as a match&lt;/li&gt;
&lt;li&gt;print the line number in the file where it was found&lt;/li&gt;
&lt;li&gt;print the two lines after the grep match&lt;/li&gt;
&lt;li&gt;print the one line before the grep match&lt;/li&gt;
&lt;li&gt;start the search in the /tmp directory&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;command&lt;/th&gt;
&lt;th&gt;notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -c 'hidden' ./myfile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;only display the number of matches in the file&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -r -l 'hidden' /tmp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;recursively search /tmp and only display the file names which contain "hidden"&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep "hidden treasure" /home/ubuntu/*.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;search only txt files&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep ab.d file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;find a single character wildcard&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep "ab.*e" file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;find a infinite repitions of a single character, word ends in e&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep "ab.*e." file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;find a infinite repitions of a single character, word ends with a single character&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep "ab[c-e]f" file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;find with a wildcard of a subset of range of characters&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="useful-parameters-for-grep"&gt;Useful parameters for grep&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-v&lt;/code&gt; = invert the match so do NOT show lines that match (typically | grep -v 'myexclude')&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-x&lt;/code&gt; = whole line match only&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-C 2&lt;/code&gt; = print two lines before and two lines after a match&lt;/p&gt;
&lt;p&gt;grep ubuntu /etc/passwd | cut -d: -f3&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;only print the user id by piping the match to cut which delimits by colon and outputs the 3rd column&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ls -t -d -1 -r path/directory/ &amp;gt;&amp;gt; oldest.m3u&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;list reverse order by timestamp
ls -t -d -1 path/directory/ | grep -v DONOTLIKE &amp;gt;&amp;gt; newest.m3u
list by timestamp (sort by modification time, newest first), list directories themselves, not their contents, only 1 level deep
pipe to grep and ignore matches of DONOTLIKE, then append output to the newest.m3u file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="grep-files-without-match"&gt;grep files without match&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;grep -L 'foobar' *
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;--files-without-match , display filenames that do not contain the string foobar&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Grep"&gt;https://en.wikipedia.org/wiki/Grep&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.gnu.org/software/grep/manual/grep.html"&gt;http://www.gnu.org/software/grep/manual/grep.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_02.html"&gt;http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_02.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="cut"&gt;cut&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ps auxwwww | grep someappname | tr -s [:space:] | cut -d\  -f11-
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;filter to only view someappname from all processes (wide) THEN shorten all whitespace to a single space THEN cut delimited by a single space (escaped by the slash) and then only prints all after the 11th field/column&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat sometext.txt | cut -f1 -d"["
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;delimiter of square bracket , only take after the first "field" token, so essentially print everything after the first occurence of a left square bracket&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://ss64.com/bash/cut.html"&gt;http://ss64.com/bash/cut.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://linux.die.net/man/1/cut"&gt;http://linux.die.net/man/1/cut&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cut-to-only-display-a-part-of-a-path"&gt;cut to only display a part of a path&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c1"&gt;# iterate through the list of subdirectories&lt;/span&gt;
&lt;span class="c1"&gt;# cut out each subdirectory name (using forward slash delimiter)&lt;/span&gt;
&lt;span class="c1"&gt;# compare a text file within to a similarly organized TEMP directory&lt;/span&gt;

&lt;span class="nv"&gt;DIRECTORY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/tomcat6/webapps/*

&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dir&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$DIRECTORY&lt;/span&gt;
&lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cut&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;diff&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;/WEB-INF/app.properties&lt;span class="w"&gt; &lt;/span&gt;/var/lib/tomcat6/TEMP/&lt;span class="nv"&gt;$NAME&lt;/span&gt;/WEB-INF/app.properties
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="awk"&gt;awk&lt;/h2&gt;
&lt;p&gt;awk to parse columns of data , some overlap with cut&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;awk -F"," '{ print $2 }' results.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;csv parsing , set the delimiter to a comma and print the second column&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="x"&gt;awk '&lt;/span&gt;&lt;span class="cp"&gt;{&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="na"&gt;print&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;&lt;span class="x"&gt;' results.txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;assuming space delimited and remove the first column but print all else in results.txt&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="x"&gt;ps aux | grep someappname | awk '&lt;/span&gt;&lt;span class="cp"&gt;{&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="na"&gt;print&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="cp"&gt;}&lt;/span&gt;&lt;span class="x"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;print everything after the nth (10th) column&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat sometext.txt | cut -f1 -d"("
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;using cut can be more effective: deleting everything after the first occurence of a left parenthesis&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'beta/dists/precise/main/binary-amd64'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;.&lt;span class="mi"&gt;2&lt;/span&gt;.&lt;span class="mi"&gt;3&lt;/span&gt;.&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AccessDenied&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;awk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{print $5}'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;sort&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;u&lt;/span&gt;

&lt;span class="nv"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nv"&gt;exclude&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.&lt;span class="nv"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'foo'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;recursively&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;directory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;foo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;but&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ignore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="nv"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;directory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;recursive search for a string, pipe the output to exclude lines that contain IP 1.2.3.4 , pipe to exclude AccessDenied, print the 5th column, sort for uniqueness&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the 5th column of results.txt contains numbers then ...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;awk&lt;/span&gt; &lt;span class="s"&gt;'{total = total + $5} END{print total}'&lt;/span&gt;

&lt;span class="n"&gt;ls&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tahl&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;awk&lt;/span&gt; &lt;span class="s"&gt;'{print $5,$6,$7,$8}'&lt;/span&gt;

&lt;span class="n"&gt;awk&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;program&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt; &lt;span class="mi"&gt;2009&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;backupCHECKUP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;
&lt;span class="mf"&gt;5.2&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt; &lt;span class="mi"&gt;2009&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;backup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;
&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt; &lt;span class="mi"&gt;2009&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;06&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mi"&gt;330&lt;/span&gt; &lt;span class="mi"&gt;2009&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;
&lt;span class="mi"&gt;253&lt;/span&gt; &lt;span class="mi"&gt;2009&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt; &lt;span class="n"&gt;daily&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;backup&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;
&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt; &lt;span class="mi"&gt;2009&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="p"&gt;..&lt;/span&gt;
&lt;span class="mf"&gt;3.9&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt; &lt;span class="mi"&gt;2009&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt; &lt;span class="n"&gt;mtrac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ini&lt;/span&gt;
&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt; &lt;span class="mi"&gt;2008&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="n"&gt;CreateProject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;

&lt;span class="n"&gt;awk&lt;/span&gt; &lt;span class="s"&gt;'/2009/ {print $5,$6,$7,$8}'&lt;/span&gt; &lt;span class="n"&gt;ls_output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that Awk recognizes the field variable $0 as representing the entire line, so this could also be written as:
    awk '/gold/ {print $0}'&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.grymoire.com/Unix/Awk.html"&gt;http://www.grymoire.com/Unix/Awk.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/gawk/manual/gawk.html"&gt;https://www.gnu.org/software/gawk/manual/gawk.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="sed"&gt;sed&lt;/h2&gt;
&lt;p&gt;sed does string substitution&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;regular&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;olditem&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;newitem&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;

&lt;span class="n"&gt;sed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;\t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;backup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;replace a space with a tab&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sed -i 's/\x85/.../g' *.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;replace a UTF-8 character (in this case the single character horizontal ellipsis) with three dots&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bartik&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/#site-slogan {&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s1"&gt;  font-size: 0.929em/#site-slogan {&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s1"&gt;  font-size: 2.929em/'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bartik&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;css&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;replace multiline with a newline using tr to translate \n to \r&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;REMEMBER&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;\&lt;span class="nv"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tab&lt;/span&gt;
\&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;newline&lt;/span&gt;
\&lt;span class="nv"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;carriage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;

&lt;span class="nv"&gt;sed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/$/\r/'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;inputfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;outputfile&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;UNIX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DOS&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;adding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CRs&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;sed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/\r$//'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;inputfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;outputfile&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DOS&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;UNIX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;removing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CRs&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;perl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;pe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/\r\n|\n|\r/\r\n/g'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;inputfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;outputfile&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Convert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DOS&lt;/span&gt;
&lt;span class="nv"&gt;perl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;pe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/\r\n|\n|\r/\n/g'&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;inputfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;outputfile&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Convert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;UNIX&lt;/span&gt;
&lt;span class="nv"&gt;perl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;pe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/\r\n|\n|\r/\r/g'&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;inputfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;outputfile&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;#&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Convert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;old&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Mac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.grymoire.com/Unix/Sed.html"&gt;http://www.grymoire.com/Unix/Sed.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/sed/manual/sed.txt"&gt;https://www.gnu.org/software/sed/manual/sed.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="dd"&gt;dd&lt;/h2&gt;
&lt;p&gt;dd can delete things very quickly (dangerous!)&lt;/p&gt;
&lt;p&gt;But a useful tool for testing upload limits or compression or any other miscellaneous file tasks is to generate a file of a specified length:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;dd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;zero&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;.&lt;span class="nv"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;zero filled 2MB file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;dd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;urandom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;random&lt;/span&gt;.&lt;span class="nv"&gt;txt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nv"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;random contents 2MB file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;hexdump&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;random&lt;/span&gt;.&lt;span class="nv"&gt;txt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;head&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notes about randomness (on linux):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/dev/urandom
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;semi-random data generated by a PRNG which is fed by the trickle of real entropy from &lt;code&gt;/dev/random&lt;/code&gt; (which blocks until the entropy pool has some randomness)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;watch -n 0 'cat /proc/sys/kernel/random/entropy_avail'

cat /dev/random &amp;gt; /dev/null
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Drain the entropy from your system&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cpuid | grep -i rand
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Look for RDRAND &lt;a href="http://en.wikipedia.org/wiki/RdRand"&gt;http://en.wikipedia.org/wiki/RdRand&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat /dev/urandom | rngtest -c 1000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;how good is your non blocking urandom?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki//dev/random"&gt;https://en.wikipedia.org/wiki//dev/random&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://linuxcommand.org/man_pages/rngtest1.html"&gt;http://linuxcommand.org/man_pages/rngtest1.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="linux"/><category term="grep"/><category term="linux"/><category term="bash"/><category term="cut"/><category term="awk"/><category term="sort"/><category term="dd"/><category term="files"/><category term="text"/><category term="ls"/><category term="sort by size"/><category term="sort by date"/><category term="exec"/></entry><entry><title>A concise summary of amazing and great ideas</title><link href="https://blog.john-pfeiffer.com/a-concise-summary-of-amazing-and-great-ideas/" rel="alternate"/><published>2011-08-08T00:00:00-07:00</published><updated>2011-08-08T00:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2011-08-08:/a-concise-summary-of-amazing-and-great-ideas/</id><summary type="html">
&lt;h3 id="test-driven-development-tdd"&gt;Test Driven Development (TDD)&lt;/h3&gt;
&lt;p&gt;Test, Code, Refactor &lt;a href="https://en.wikipedia.org/wiki/Test-driven_development"&gt;https://en.wikipedia.org/wiki/Test-driven_development&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="lean"&gt;Lean&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Lean: Identify Value -&amp;gt; Breakdown Steps -&amp;gt; Continuous Flow -&amp;gt; Reduce Waste&lt;/li&gt;
&lt;li&gt;Lean Startup: Measure, Learn, Build &lt;a href="http://theleanstartup.com/principles"&gt;http://theleanstartup.com/principles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="better-than-free"&gt;Better than Free&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://kk.org/thetechnium/better-than-fre/"&gt;https://kk.org/thetechnium/better-than-fre/&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;immediacy&lt;/li&gt;
&lt;li&gt;personalization&lt;/li&gt;
&lt;li&gt;interpretation/support&lt;/li&gt;
&lt;li&gt;authenticity&lt;/li&gt;
&lt;li&gt;accessibility&lt;/li&gt;
&lt;li&gt;embodiment&lt;/li&gt;
&lt;li&gt;patronage&lt;/li&gt;
&lt;li&gt;findability&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="persuasion"&gt;Persuasion …&lt;/h3&gt;</summary><content type="html">
&lt;h3 id="test-driven-development-tdd"&gt;Test Driven Development (TDD)&lt;/h3&gt;
&lt;p&gt;Test, Code, Refactor &lt;a href="https://en.wikipedia.org/wiki/Test-driven_development"&gt;https://en.wikipedia.org/wiki/Test-driven_development&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="lean"&gt;Lean&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Lean: Identify Value -&amp;gt; Breakdown Steps -&amp;gt; Continuous Flow -&amp;gt; Reduce Waste&lt;/li&gt;
&lt;li&gt;Lean Startup: Measure, Learn, Build &lt;a href="http://theleanstartup.com/principles"&gt;http://theleanstartup.com/principles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="better-than-free"&gt;Better than Free&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://kk.org/thetechnium/better-than-fre/"&gt;https://kk.org/thetechnium/better-than-fre/&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;immediacy&lt;/li&gt;
&lt;li&gt;personalization&lt;/li&gt;
&lt;li&gt;interpretation/support&lt;/li&gt;
&lt;li&gt;authenticity&lt;/li&gt;
&lt;li&gt;accessibility&lt;/li&gt;
&lt;li&gt;embodiment&lt;/li&gt;
&lt;li&gt;patronage&lt;/li&gt;
&lt;li&gt;findability&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="persuasion"&gt;Persuasion&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Robert_Cialdini"&gt;https://en.wikipedia.org/wiki/Robert_Cialdini&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;reciprocity &lt;/li&gt;
&lt;li&gt;commitment/consistency&lt;/li&gt;
&lt;li&gt;social proof&lt;/li&gt;
&lt;li&gt;authority&lt;/li&gt;
&lt;li&gt;liking&lt;/li&gt;
&lt;li&gt;scarcity&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="competition-and-profit"&gt;Competition and Profit&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Porter_five_forces_analysis"&gt;https://en.wikipedia.org/wiki/Porter_five_forces_analysis&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Threat of new competition (barriers to entry, customer loyalty, desirability of that industry/biz model)&lt;/li&gt;
&lt;li&gt;Threat of substitute products or services (switching costs, quality, compatibility)&lt;/li&gt;
&lt;li&gt;Bargaining power of customers (switching costs, market options, dependency for other services)&lt;/li&gt;
&lt;li&gt;Bargaining power of suppliers (switching costs, supplier choice, supplier becoming competitor)&lt;/li&gt;
&lt;li&gt;Intensity of competitive rivalry (innovation, branding, economies of scale)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="schellings-segregation-model"&gt;Schelling's segregation model&lt;/h3&gt;
&lt;p&gt;Micromotives and macrobehavior , &lt;a href="https://en.wikipedia.org/wiki/Thomas_Schelling#Models_of_segregation"&gt;https://en.wikipedia.org/wiki/Thomas_Schelling#Models_of_segregation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Granovetter threshold model for peer effect on collective behavior and Strength of Weak Ties (aka how LinkedIn gets you a new job), &lt;a href="https://en.wikipedia.org/wiki/Mark_Granovetter"&gt;https://en.wikipedia.org/wiki/Mark_Granovetter&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="cognitive-biases"&gt;Cognitive Biases&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Bandwagon Effect (Groupthink)&lt;/li&gt;
&lt;li&gt;Confirmation Bias: search for and interpret information and memories that support preconceptions&lt;/li&gt;
&lt;li&gt;Gambler's Fallacy: future probabilities are affected by previous outcomes&lt;/li&gt;
&lt;li&gt;Negativity Bias: paying more attention to bad news&lt;/li&gt;
&lt;li&gt;Neglect of Probability: disregarding probabilities when making a decision (risk of flying versus driving)&lt;/li&gt;
&lt;li&gt;Observational Selection Bias (Frequency Illusion): noting something previously ignored results in a misconception that it has increased in frequency &lt;/li&gt;
&lt;li&gt;Projection Bias: wrongly presuming others think like us&lt;/li&gt;
&lt;li&gt;Status Quo Bias: things should stay the same&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="logical-fallacies-and-disinformation"&gt;Logical Fallacies and Disinformation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Appeal to probability: because something could happen it is inevitable that it will happen (see Gambler's Fallacy and Neglect of Probability)&lt;/li&gt;
&lt;li&gt;Silence, Indignant, Rumors, Straw Man, Ad Hominem, Hit and Run, Question Motives, Invoke Authority, Play Dumb, "That's old news", Confess to a lesser item and "come clean", Enigma, Rube Goldberg Logic, Demand a complete solution, Fit the facts to alternate conclusions, Remove witnesses/evidence, Change the subject, Antagonize, Ignore proof and demand impossible proof, False evidence/facts, Loudly call for a separate investigation (ideally either biased or with confidential findings), Manufacture a new truth, Larger distractions, Silence critics, Lie low&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;note that these topics are often associated with paranoid conspiracy theories, "Rules of Disinformation"&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="keys-to-being-successful"&gt;Keys to being successful&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;lists&lt;/li&gt;
&lt;li&gt;organize (categorize)&lt;/li&gt;
&lt;li&gt;prioritize&lt;/li&gt;
&lt;li&gt;schedule&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;brainstorm goals and feelings: "say what you're going to do and then do what you say"&lt;/p&gt;
&lt;h4 id="concentrate-iterate-automate-validate-appreciate"&gt;Concentrate, Iterate, Automate, Validate, Appreciate&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Software version: core tech strengths &amp;amp; problem, quick releases, automate, test!, style? + recognize the contributions&lt;/li&gt;
&lt;li&gt;Military version: core strength and enemy weaknesses, rapid short executions, make excellence a reflex, check for brittleness, engender loyalty&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="7-habits-of-highly-effective-people"&gt;7 habits of highly effective people&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;be proactive&lt;/li&gt;
&lt;li&gt;"begin with the end in mind" (envision the goal)&lt;/li&gt;
&lt;li&gt;"put first things first" (order and prioritize)&lt;/li&gt;
&lt;li&gt;"think win-win" (good outcomes for everyone)&lt;/li&gt;
&lt;li&gt;"Seek First to Understand, Then to be Understood" (listen, then persuade)&lt;/li&gt;
&lt;li&gt;"synergize" (teamwork)&lt;/li&gt;
&lt;li&gt;"sharpen the saw" (sustainable balance)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/The_7_Habits_of_Highly_Effective_People"&gt;https://en.wikipedia.org/wiki/The_7_Habits_of_Highly_Effective_People&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="maslows-hierarchy-of-needs"&gt;Maslow's Hierarchy of Needs&lt;/h4&gt;
&lt;p&gt;The lowest levels of the pyramid must be satisfied before people can focus and succeed at higher levels.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Actualization&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;_______Esteem________&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;_____Love&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;Belonging______&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;__________Safety___________&lt;/span&gt;
&lt;span class="nx"&gt;_________Physiological_________&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Maslow's_hierarchy_of_needs"&gt;https://en.wikipedia.org/wiki/Maslow's_hierarchy_of_needs&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="important-software-concepts"&gt;Important Software Concepts&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Do Not Repeat Yourself (DRY)&lt;/li&gt;
&lt;li&gt;Model View Controller (MVC)&lt;/li&gt;
&lt;li&gt;Atomic Consistent Isolation Durability (ACID)&lt;/li&gt;
&lt;li&gt;Abstraction Polymorphism (overloading, inheritance, overriding interface) ,  Inheritance , Encapsulation&lt;/li&gt;
&lt;li&gt;Consistency Availability Partition tolerance vs Basically Available Soft-State with Eventual consistency&lt;/li&gt;
&lt;li&gt;Nondeterministic Polynomial ... NP-hard &lt;a href="https://en.wikipedia.org/wiki/NP-hard"&gt;https://en.wikipedia.org/wiki/NP-hard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;NP-complete (subset sum problem can be verified) &lt;a href="https://en.wikipedia.org/wiki/NP-complete"&gt;https://en.wikipedia.org/wiki/NP-complete&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;co-NP (verifier of "no" answer")&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Timeline_of_algorithms"&gt;https://en.wikipedia.org/wiki/Timeline_of_algorithms&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="amdahls-law"&gt;Amdahl's Law&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;A system cannot be sped up by parallelization more than the inherently serial steps &lt;a href="https://en.wikipedia.org/wiki/Amdahl%27s_law"&gt;https://en.wikipedia.org/wiki/Amdahl%27s_law&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;So benchmark your system, then determine what parts can be parallelized and how much that will improve the result and how much will it cost to do so&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="conways-law"&gt;Conway's Law&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;The system design produced by an organization will reflect the organization's communication structure. &lt;a href="https://www.melconway.com/Home/Conways_Law.html"&gt;https://www.melconway.com/Home/Conways_Law.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.thoughtworks.com/insights/blog/demystifying-conways-law"&gt;https://www.thoughtworks.com/insights/blog/demystifying-conways-law&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Possibly disastrous results when combined with Groupthink &lt;a href="https://en.wikipedia.org/wiki/Groupthink"&gt;https://en.wikipedia.org/wiki/Groupthink&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Commonly referred to when considering how adding a new person or new team to organization will affect productivity&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="brooks-law"&gt;Brooks' Law&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Adding resources (people) later in a project will make it even later &lt;a href="https://en.wikipedia.org/wiki/Brooks%27s_law"&gt;https://en.wikipedia.org/wiki/Brooks%27s_law&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;A decent observation given the above "laws": if a task has serial parts adding people (parallelization) will not speed it up AND every person will have to interface &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="moores-law"&gt;Moore's Law&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Computing power will double (or become cheaper by half) every two years &lt;a href="https://en.wikipedia.org/wiki/Moore%27s_law"&gt;https://en.wikipedia.org/wiki/Moore%27s_law&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Sustained in part by improvements in complimentary technologies like Memory, Storage, Cooling, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;At a certain point in the future potentially only possible using parallel computing but with an increased coordination cost (including software that leverages parellization)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="postels-law"&gt;Postel's Law&lt;/h4&gt;
&lt;p&gt;Be conservative in what you do, be liberal in what you accept from others&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.m.wikipedia.org/wiki/Robustness_principle"&gt;https://en.m.wikipedia.org/wiki/Robustness_principle&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="laws-of-unix"&gt;Laws of Unix&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Unix_philosophy#Eric_Raymond.E2.80.99s_17_Unix_Rules"&gt;https://en.wikipedia.org/wiki/Unix_philosophy#Eric_Raymond.E2.80.99s_17_Unix_Rules&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Modularity: Write simple parts connected by clean interfaces.&lt;/li&gt;
&lt;li&gt;Clarity: Clarity is better than cleverness.&lt;/li&gt;
&lt;li&gt;Composition: Design programs to be connected with other programs.&lt;/li&gt;
&lt;li&gt;Separation: Separate policy from mechanism; separate interfaces from engines.&lt;/li&gt;
&lt;li&gt;Simplicity: Design for simplicity; add complexity only where you must.&lt;/li&gt;
&lt;li&gt;Parsimony: Write a big program only when it is clear by demonstration that nothing else will do.&lt;/li&gt;
&lt;li&gt;Transparency: Design for visibility to make inspection and debugging easier.&lt;/li&gt;
&lt;li&gt;Robustness: Robustness is the child of transparency and simplicity.&lt;/li&gt;
&lt;li&gt;Representation: Fold knowledge into data, so program logic can be stupid and robust.&lt;/li&gt;
&lt;li&gt;Least Surprise: In interface design, always do the least surprising thing.&lt;/li&gt;
&lt;li&gt;Silence: When a program has nothing surprising to say, it should say nothing.&lt;/li&gt;
&lt;li&gt;Repair: Repair what you can, but when you must fail, fail noisily and as soon as possible.&lt;/li&gt;
&lt;li&gt;Economy: Programmer time is expensive; conserve it in preference to machine time.&lt;/li&gt;
&lt;li&gt;Generation: Avoid hand-hacking; write programs to write programs when you can.&lt;/li&gt;
&lt;li&gt;Optimization: Prototype before polishing. Get it working before you optimize it.&lt;/li&gt;
&lt;li&gt;Diversity: Distrust all claims for one true way.&lt;/li&gt;
&lt;li&gt;Extensibility: Design for the future, because it will be here sooner than you think. (Or, to put it another way, your creations will last longer than you think!)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="clean-code"&gt;Clean Code&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Source Code is for humans, make it easy to read and understand&lt;/li&gt;
&lt;li&gt;The code is the authoritative source (comments add context)&lt;/li&gt;
&lt;li&gt;Leave the campground cleaner than you found it&lt;/li&gt;
&lt;li&gt;Tests reveal what the code outputs; clean code runs all of the tests&lt;/li&gt;
&lt;li&gt;Meaningful Names&lt;/li&gt;
&lt;li&gt;Functions: A minimum number of parameters and the smaller the better&lt;/li&gt;
&lt;li&gt;Open - Close principle&lt;/li&gt;
&lt;li&gt;Single Responsibility (do one thing, and do it well)&lt;/li&gt;
&lt;li&gt;No Duplication (DRY)&lt;/li&gt;
&lt;li&gt;Objects allow modularity, Boundaries keep you sane&lt;/li&gt;
&lt;li&gt;Separate Constructing a System from Using it (and Initialization from Runtime)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="fallacies-of-distributed-computing"&gt;Fallacies of Distributed Computing&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing"&gt;https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The network is reliable.&lt;/li&gt;
&lt;li&gt;Latency is zero.&lt;/li&gt;
&lt;li&gt;Bandwidth is infinite.&lt;/li&gt;
&lt;li&gt;The network is secure.&lt;/li&gt;
&lt;li&gt;Topology doesn't change.&lt;/li&gt;
&lt;li&gt;There is one administrator.&lt;/li&gt;
&lt;li&gt;Transport cost is zero.&lt;/li&gt;
&lt;li&gt;The network is homogeneous.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="how-complex-systems-fail"&gt;How Complex Systems Fail&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://web.mit.edu/2.75/resources/random/How%20Complex%20Systems%20Fail.pdf"&gt;http://web.mit.edu/2.75/resources/random/How%20Complex%20Systems%20Fail.pdf&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Complex systems are intrinsically hazardous systems&lt;/li&gt;
&lt;li&gt;Complex systems are heavily and successfully defended against failure&lt;/li&gt;
&lt;li&gt;Catastrophe requires multiple failures – single point failures are not enough&lt;/li&gt;
&lt;li&gt;Complex systems contain changing mixtures of failures latent within them&lt;/li&gt;
&lt;li&gt;Complex systems run in degraded mode&lt;/li&gt;
&lt;li&gt;Catastrophe is always just around the corner&lt;/li&gt;
&lt;li&gt;Post-accident attribution accident to a ‘root cause’ is fundamentally wrong&lt;/li&gt;
&lt;li&gt;Hindsight biases post-accident assessments of human performance&lt;/li&gt;
&lt;li&gt;Human operators have dual roles: as producers &amp;amp; as defenders against failure&lt;/li&gt;
&lt;li&gt;All practitioner actions are gambles&lt;/li&gt;
&lt;li&gt;Actions at the sharp end resolve all ambiguity&lt;/li&gt;
&lt;li&gt;Human practitioners are the adaptable element of complex systems&lt;/li&gt;
&lt;li&gt;Human expertise in complex systems is constantly changing&lt;/li&gt;
&lt;li&gt;Change introduces new forms of failure&lt;/li&gt;
&lt;li&gt;Views of ‘cause’ limit the effectiveness of defenses against future events&lt;/li&gt;
&lt;li&gt;Safety is a characteristic of systems and not of their components&lt;/li&gt;
&lt;li&gt;People continuously create safety&lt;/li&gt;
&lt;li&gt;Failure free operations require experience with failure&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="why-do-computers-stop-and-what-can-be-done-about-it"&gt;Why do computers stop and what can be done about it&lt;/h3&gt;
&lt;p&gt;Jim Gray: &lt;a href="http://www.hpl.hp.com/techreports/tandem/TR-85.7.pdf"&gt;http://www.hpl.hp.com/techreports/tandem/TR-85.7.pdf&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="dickersons-hierarchy-of-reliability"&gt;Dickerson's Hierarchy of Reliability&lt;/h3&gt;
&lt;p&gt;Product
Development
Capacity Planning
Testing and Release Procedures
Postmortem and Root Cause Analysis
Incident Response
Monitoring&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.google.com/drawings/d/1kshrK2RLkW-XV8enmWZxeRFRgADj6d4Ru_w5txz_k9I/edit"&gt;https://docs.google.com/drawings/d/1kshrK2RLkW-XV8enmWZxeRFRgADj6d4Ru_w5txz_k9I/edit&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="designerscreators-of-programming-languages"&gt;Designers/Creators of Programming Languages&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Language&lt;/th&gt;
&lt;th style="text-align: center;"&gt;Creator/Designer&lt;/th&gt;
&lt;th style="text-align: center;"&gt;Year&lt;/th&gt;
&lt;th style="text-align: center;"&gt;more info&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Fortran&lt;/td&gt;
&lt;td style="text-align: center;"&gt;John Backus&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  1957&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  &lt;a href="https://en.wikipedia.org/wiki/Fortran"&gt;https://en.wikipedia.org/wiki/Fortran&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Lisp&lt;/td&gt;
&lt;td style="text-align: center;"&gt;John McCarthy&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  1958&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  &lt;a href="https://en.wikipedia.org/wiki/Lisp_(programming_language)"&gt;https://en.wikipedia.org/wiki/Lisp_(programming_language)&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;C&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Dennis Ritchie&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  1972&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  &lt;a href="https://en.wikipedia.org/wiki/C_(programming_language)"&gt;https://en.wikipedia.org/wiki/C_(programming_language)&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;C++&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Bjarne Stroustrup&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  1983&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  &lt;a href="https://en.wikipedia.org/wiki/C%2B%2B"&gt;https://en.wikipedia.org/wiki/C%2B%2B&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Perl&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Larry Wall&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  1987&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  &lt;a href="https://en.wikipedia.org/wiki/Perl"&gt;https://en.wikipedia.org/wiki/Perl&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Python&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Guido van Roosum&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  1991&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  &lt;a href="https://en.wikipedia.org/wiki/Python_(programming_language)"&gt;https://en.wikipedia.org/wiki/Python_(programming_language)&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Java&lt;/td&gt;
&lt;td style="text-align: center;"&gt;James Gosling&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  1995&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  &lt;a href="https://en.wikipedia.org/wiki/Java_(programming_language)"&gt;https://en.wikipedia.org/wiki/Java_(programming_language)&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;PHP&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Rasmus Lerdorf&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  1995&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  &lt;a href="https://en.wikipedia.org/wiki/PHP"&gt;https://en.wikipedia.org/wiki/PHP&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Javascript&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Brendan Eich&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  1995&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  &lt;a href="https://en.wikipedia.org/wiki/JavaScript"&gt;https://en.wikipedia.org/wiki/JavaScript&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Ruby&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Yukihiro Matsumoto&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  1995&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  &lt;a href="https://en.wikipedia.org/wiki/Ruby_(programming_language)"&gt;https://en.wikipedia.org/wiki/Ruby_(programming_language)&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;Go&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Robert Griesemer, Rob Pike, Ken Thompson&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  2009&lt;/td&gt;
&lt;td style="text-align: center;"&gt;  &lt;a href="https://en.wikipedia.org/wiki/Go_(programming_language)"&gt;https://en.wikipedia.org/wiki/Go_(programming_language)&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="a-quick-history-of-software-in-ascii"&gt;A quick history of software (in ascii)&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;hardcoded&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hardware&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENIAC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;von&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;neumann&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;architecture&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;stored&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;programs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;mainframes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;custom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;punch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;procedural&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fortran&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;oriented&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;simula&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="kr"&gt;parallel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;programming&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Artificial&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Intelligence&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;writes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;self&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;adapting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Domain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Specific&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Langauges&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;everything&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Start by reading all of the following to nitpick how the above is fast and loose with history and the truth...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Computer"&gt;https://en.wikipedia.org/wiki/Computer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/ENIAC"&gt;https://en.wikipedia.org/wiki/ENIAC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Von_Neumann_architecture"&gt;https://en.wikipedia.org/wiki/Von_Neumann_architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Programming_paradigm"&gt;https://en.wikipedia.org/wiki/Programming_paradigm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Object-oriented_programming#History"&gt;https://en.wikipedia.org/wiki/Object-oriented_programming#History&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Parallel_computing#Software"&gt;https://en.wikipedia.org/wiki/Parallel_computing#Software&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Concurrent_computing"&gt;https://en.wikipedia.org/wiki/Concurrent_computing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="hackers-jargon"&gt;Hacker's Jargon&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.catb.org/jargon/oldversions/"&gt;http://www.catb.org/jargon/oldversions/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="puzzles"/><category term="thoughts"/><category term="tdd"/><category term="lean"/><category term="persuasion"/><category term="great"/><category term="efficiency"/><category term="laws"/><category term="software"/></entry><entry><title>Time for Programmers</title><link href="https://blog.john-pfeiffer.com/time-for-programmers/" rel="alternate"/><published>2011-06-05T21:20:00-07:00</published><updated>2011-06-05T21:20:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2011-06-05:/time-for-programmers/</id><summary type="html">
&lt;p&gt;Computer (Unix / POSIX) time starts 1970-01-01 00:00:00 UTC &lt;a href="http://en.wikipedia.org/wiki/Unix_time"&gt;http://en.wikipedia.org/wiki/Unix_time&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;An excellent article about time, especially for java programmers, &lt;a href="http://www.odi.ch/prog/design/datetime.php"&gt;http://www.odi.ch/prog/design/datetime.php&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Inside the "river of time" measurement is absurd, but Physicists have spacetime, &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"...cycles of radiation corresponding to …&lt;/p&gt;&lt;/blockquote&gt;</summary><content type="html">
&lt;p&gt;Computer (Unix / POSIX) time starts 1970-01-01 00:00:00 UTC &lt;a href="http://en.wikipedia.org/wiki/Unix_time"&gt;http://en.wikipedia.org/wiki/Unix_time&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;An excellent article about time, especially for java programmers, &lt;a href="http://www.odi.ch/prog/design/datetime.php"&gt;http://www.odi.ch/prog/design/datetime.php&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Inside the "river of time" measurement is absurd, but Physicists have spacetime, &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"...cycles of radiation corresponding to the transition between the two electron spin energy levels of the ground state of the 133 Caesium atom".&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;24 hours, UTC and NTP can synchronize the world (especially servers!), but days, calendars, time zones, weeks, etc. will drive you crazy, so think carefully and use the utility libraries!&lt;/p&gt;
&lt;h3 id="java-datetime-timezone-example"&gt;Java datetime timezone example&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.Calendar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.TimeZone&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.text.DateFormat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.text.SimpleDateFormat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// not thread-safe&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SimpleDateFormat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dfm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SimpleDateFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"yyyy-MM-dd"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;DateFormat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dfm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SimpleDateFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"yyyy-MM-dd HH:mm:ss"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;dfm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTimeZone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimeZone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTimeZone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Europe/Zurich"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dfm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2007-02-26 20:15:00"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="yesterday-in-python"&gt;Yesterday in python&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;datetime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;
&lt;span class="n"&gt;yesterday&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromordinal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toordinal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="dateutil-and-helper-functions"&gt;dateutil and helper functions&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dateutil.parser&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;
&lt;span class="c1"&gt;# http://labix.org/python-dateutil  (for google app engine put the source directory at the root project level)&lt;/span&gt;

&lt;span class="n"&gt;myd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Thu, 11 Jul 2013 05:01:21 -0700'&lt;/span&gt;
&lt;span class="n"&gt;datetime_obj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dateutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;seconds_to_datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromtimestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;datetime_string_to_seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;date_str&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;datetime_obj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dateutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;date_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Thu, 11 Jul 2013 05:01:21 -0700&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Utility&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime_to_seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime_obj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;datetime_to_seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime_obj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mktime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timetuple&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="pytz-for-timezones"&gt;pytz for timezones&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pytz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# sometimes requires complex installation, easy_install --upgrade pytz&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;datetime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pytz&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"UTC"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;

&lt;span class="n"&gt;date_utc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pytz&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UTC"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;date_utc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="time-and-datetime-tuples"&gt;time and datetime tuples&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="n"&gt;mytime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Mon Apr 07 13:05:55 PDT 2014"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;%a&lt;/span&gt;&lt;span class="s2"&gt; %b &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt; %H:%M:%S %Z %Y"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# time.struct_time(tm_year=2014, tm_mon=4, tm_mday=7, tm_hour=13, tm_min=5, tm_sec=55, tm_wday=0, tm_yday=97, tm_isdst=1)&lt;/span&gt;
&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mktime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mytime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# 1396901155.0&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt; %H:%M:%S"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mytime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# 2014-04-07 13:05:55&lt;/span&gt;

&lt;span class="n"&gt;time_tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2008&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;317&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt; %H:%M:%S"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time_tuple&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# 2008-11-10 17:53:59&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;datetime&lt;/span&gt;
&lt;span class="n"&gt;date_object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2008&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;date_object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt; %H:%M:%S"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# 2008-11-10 17:53:59&lt;/span&gt;

&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1226527167.595983&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromtimestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# repr prints with limits on sizes of objects&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;calendar&lt;/span&gt;
&lt;span class="n"&gt;time_tuple_utc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2008&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;317&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# time tuple in utc time to timestamp&lt;/span&gt;
&lt;span class="n"&gt;timestamp_utc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;calendar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timegm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time_tuple_utc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp_utc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;#-------------------------------------------------&lt;/span&gt;
&lt;span class="n"&gt;time_tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2008&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;317&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;datetime_object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time_tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime_object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;date_string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2008-11-10 17:53:59"&lt;/span&gt;
&lt;span class="n"&gt;datetime_object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt; %H:%M:%S"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime_object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1226527167.595983&lt;/span&gt;
&lt;span class="n"&gt;datetime_object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromtimestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# local time&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime_object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1226527167.595983&lt;/span&gt;
&lt;span class="n"&gt;datetime_object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utcfromtimestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime_object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#-------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# conversions to time tuples&lt;/span&gt;

&lt;span class="n"&gt;datetime_object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2008&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;time_tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datetime_object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timetuple&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time_tuple&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;date_str&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2008-11-10 17:53:59"&lt;/span&gt;
&lt;span class="n"&gt;time_tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt; %H:%M:%S"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time_tuple&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1226527167.595983&lt;/span&gt;
&lt;span class="n"&gt;local_time_tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# local time&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local_time_tuple&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;utc_time_tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gmtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# UTC&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;utc_time_tuple&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;#-------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# conversions to timestamps&lt;/span&gt;

&lt;span class="c1"&gt;# time tuple in local time to timestamp&lt;/span&gt;
&lt;span class="n"&gt;time_tuple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2008&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;317&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mktime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time_tuple&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# time tuple in utc time to timestamp&lt;/span&gt;
&lt;span class="n"&gt;time_tuple_utc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2008&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;317&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;timestamp_utc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;calendar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timegm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time_tuple_utc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp_utc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#-------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# results&lt;/span&gt;
&lt;span class="c1"&gt;#-------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# 2008-11-10 17:53:59&lt;/span&gt;
&lt;span class="c1"&gt;# 2008-11-12 13:51:18&lt;/span&gt;
&lt;span class="c1"&gt;# datetime.datetime(2008, 11, 12, 13, 51, 18)&lt;/span&gt;
&lt;span class="c1"&gt;# datetime.datetime(2008, 11, 10, 17, 53, 59)&lt;/span&gt;
&lt;span class="c1"&gt;# datetime.datetime(2008, 11, 12, 13, 59, 27, 595983)&lt;/span&gt;
&lt;span class="c1"&gt;# datetime.datetime(2008, 11, 12, 21, 59, 27, 595983)&lt;/span&gt;
&lt;span class="c1"&gt;# (2008, 11, 10, 17, 53, 59, 0, 315, -1)&lt;/span&gt;
&lt;span class="c1"&gt;# (2008, 11, 10, 17, 53, 59, 0, 315, -1)&lt;/span&gt;
&lt;span class="c1"&gt;# (2008, 11, 12, 21, 59, 27, 2, 317, 0)&lt;/span&gt;
&lt;span class="c1"&gt;# (2008, 11, 12, 13, 59, 27, 2, 317, 0)&lt;/span&gt;
&lt;span class="c1"&gt;# 1226527167.0&lt;/span&gt;
&lt;span class="c1"&gt;# 1226498367&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="programming"/><category term="time"/><category term="epoch"/><category term="java"/><category term="python"/></entry><entry><title>Amazon SES on EC2 free tier to search for a kitteh!</title><link href="https://blog.john-pfeiffer.com/amazon-ses-on-ec2-free-tier-to-search-for-a-kitteh/" rel="alternate"/><published>2011-02-28T00:07:00-08:00</published><updated>2011-02-28T00:07:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2011-02-28:/amazon-ses-on-ec2-free-tier-to-search-for-a-kitteh/</id><summary type="html">
&lt;h3 id="mission-hourly-poll-of-a-website"&gt;Mission: hourly poll of a website&lt;/h3&gt;
&lt;p&gt;...to find out if the Kitteh is available for adoption and immediate email notification if Kitteh is found.&lt;/p&gt;
&lt;p&gt;Estimated time to complete: between 15 minutes and hours (depending on setting up your EC2 instance, SES service, etc.)&lt;/p&gt;
&lt;p&gt;Skills: Amazon EC2 setup, SSH, centos yum …&lt;/p&gt;</summary><content type="html">
&lt;h3 id="mission-hourly-poll-of-a-website"&gt;Mission: hourly poll of a website&lt;/h3&gt;
&lt;p&gt;...to find out if the Kitteh is available for adoption and immediate email notification if Kitteh is found.&lt;/p&gt;
&lt;p&gt;Estimated time to complete: between 15 minutes and hours (depending on setting up your EC2 instance, SES service, etc.)&lt;/p&gt;
&lt;p&gt;Skills: Amazon EC2 setup, SSH, centos yum, bash, wget, cronjob&lt;/p&gt;
&lt;h3 id="amazon-free-services-tier"&gt;Amazon Free services tier&lt;/h3&gt;
&lt;p&gt;If you have an Amazon EC2 instance running (e.g. EC2 Linux Micro Instance in Free Tier = centos!)&lt;/p&gt;
&lt;p&gt;(And you're not running over the GET/POST upload/download free tier bandwidths)&lt;/p&gt;
&lt;p&gt;(If you don't know how to setup a quick Amazon Linux Micro Instance in the free tier search this blog for more info)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sign up for SES (then receive a verification email for your Amazon AWS Account)&lt;/li&gt;
&lt;li&gt;Account Security Credentials (for AWS access identifiers)  &lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use nano or vi to create a file "aws-credentials" (Amazon's sample below)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;AWSAccessKeyId=022QF06E7MXBSH9DHM
AWSSecretKey=kWcrlUX5JEDGM/LtmEENI/aVmYvHNif5zB+d9+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Download the example perl scripts via: &lt;a href="http://aws.amazon.com/code/Amazon-SES"&gt;http://aws.amazon.com/code/Amazon-SES&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;wget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;catalog&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amazonaws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;AmazonSES&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;02.&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;
&lt;span class="n"&gt;unzip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AmazonSES&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;02.&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;
&lt;span class="n"&gt;chmod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;/*.&lt;/span&gt;&lt;span class="n"&gt;pl&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ses&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;youreemail&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="amazon-ec2-missing-some-perl"&gt;Amazon EC2 Missing some perl&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="s"&gt;"Can't locate XML/LibXML.pm in @INC (@INC contains:&lt;/span&gt;

&lt;span class="s"&gt;/usr/local/lib64/perl5&lt;/span&gt;
&lt;span class="s"&gt;/usr/local/share/perl5       &lt;/span&gt;
&lt;span class="s"&gt;/usr/local/share/perl5 &lt;/span&gt;
&lt;span class="s"&gt;/usr/lib64/perl5   &lt;/span&gt;
&lt;span class="s"&gt;/usr/share/perl5&lt;/span&gt;
&lt;span class="s"&gt;/usr/share/perl5&lt;/span&gt;
&lt;span class="s"&gt;/usr/lib64/perl5&lt;/span&gt;
&lt;span class="s"&gt;/usr/share/perl5&lt;/span&gt;
&lt;span class="s"&gt;/usr/local/lib64/perl5/site_perl/5.10.0/x86_64-linux-thread-multi&lt;/span&gt;

&lt;span class="s"&gt;/usr/local/lib/perl5/site_perl/5.10.0&lt;/span&gt;
&lt;span class="s"&gt;/usr/lib64/perl5/vendor_perl/5.10.0/x86_64-linux-thread-multi&lt;/span&gt;
&lt;span class="s"&gt;/usr/lib/perl5/vendor_perl&lt;/span&gt;
&lt;span class="s"&gt;/usr/lib/perl5/site_perl .) at ./ses-verify-email-address.pl line 26.&lt;/span&gt;

&lt;span class="s"&gt;BEGIN failed--compilation aborted at ./ses-verify-email-address.pl line 26."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;THANKS AMAZON! Using their preconfigured Instance means they don't have all of the Perl packages installed...&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;sudo yum install perl-XML-LibXML perl-digest-SHA&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo yum provides */SHA.pm&lt;/code&gt;&lt;blockquote&gt;
&lt;p&gt;tells me what other packages I might have missed...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo yum search perl-Digest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo yum install perl-Digest-SHA&lt;/code&gt;&lt;blockquote&gt;
&lt;p&gt;What a difference a D versus d makes!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;sudo yum perl-libxml-perl libxml2-devel perl-IO-Socket-SSL libxslt-devel ?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Debian: &lt;code&gt;sudo apt-get install libio-socket-ssl-perl libxml-libxml-perl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tail /var/log/maillog&lt;/code&gt; for troubleshooting sendmail...&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="verify-an-ses-linked-email-address-by-running-the-perl-script"&gt;Verify an SES linked email address by running the perl script&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;/home/ec2-user/bin/ses-verify-email-address.pl -k aws-credentials -v youreemail@example.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Use the email account you gave above for verification and click on the link...  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You have successfully verified an email address with Amazon Simple Email Service.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="n"&gt;amazonses&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ses&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="n"&gt;amazonses&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;"Test AWS"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;youremail&lt;/span&gt;&lt;span class="nv"&gt;@example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;youremail&lt;/span&gt;&lt;span class="nv"&gt;@example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;secondemail&lt;/span&gt;&lt;span class="nv"&gt;@example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="n"&gt;kittysearch&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="searchsh"&gt;Search.sh&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;#/bin/bash&lt;/span&gt;
wget&lt;span class="w"&gt; &lt;/span&gt;-O&lt;span class="w"&gt; &lt;/span&gt;~/kittysearch/page1.htm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'http://adopt.hssv.org/search/searchResults.asp?task=search&amp;amp;searchid=&amp;amp;advanced=&amp;amp;s=adoption&amp;amp;animalType=2%2C15&amp;amp;statusID=3&amp;amp;state=&amp;amp;regionID=&amp;amp;submitbtn=Find+Animals'&lt;/span&gt;
wget&lt;span class="w"&gt; &lt;/span&gt;-O&lt;span class="w"&gt; &lt;/span&gt;~/kittysearch/page2.htm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'http://adopt.hssv.org/search/searchResults.asp?tpage=2&amp;amp;task=search&amp;amp;searchid=&amp;amp;advanced=&amp;amp;s=&amp;amp;animalType=2,15&amp;amp;statusID=3&amp;amp;state=&amp;amp;regionID=&amp;amp;submitbtn=Find+Animals'&lt;/span&gt;
wget&lt;span class="w"&gt; &lt;/span&gt;-O&lt;span class="w"&gt; &lt;/span&gt;~/kittysearch/page3.htm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'http://adopt.hssv.org/search/searchResults.asp?tpage=3&amp;amp;task=search&amp;amp;searchid=&amp;amp;advanced=&amp;amp;s=&amp;amp;animalType=2,15&amp;amp;statusID=3&amp;amp;state=&amp;amp;regionID=&amp;amp;submitbtn=Find+Animals'&lt;/span&gt;
grep&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bandit"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/kittysearch/page1.htm&lt;span class="w"&gt; &lt;/span&gt;~/kittysearch/page2.htm&lt;span class="w"&gt; &lt;/span&gt;~/kittysearch/page3.htm&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;~/kittysearch/result.txt

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;~/kittysearch/result.txt&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# must move to the directory to use the SES.pm&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/amazonses/bin
&lt;span class="w"&gt;    &lt;/span&gt;./ses-send-email.pl&lt;span class="w"&gt; &lt;/span&gt;-k&lt;span class="w"&gt; &lt;/span&gt;~/amazonses/bin/aws-credentials&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Test AWS"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;myemail@domain.com&lt;span class="w"&gt; &lt;/span&gt;myemail@domain.com,secondrecipient@domain.com&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;~/kittysearch/result.txt
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="c1"&gt;# http://docs.amazonwebservices.com/ses/latest/DeveloperGuide/ for full details about email.pl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="trigger-the-search-with-cron"&gt;Trigger the search with cron&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;crontab -e&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;i key to enter input in vi&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;55&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;kittysearch&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;kittysearch&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;escape key gets : then x to save and quit&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="troubleshooting-cannot-locate-sespm"&gt;Troubleshooting "Cannot locate SES.pm"&lt;/h3&gt;
&lt;p&gt;Running the script from a different directory or CRON gets the error: "Can't locate SES.pm in @INC"&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cp /home/ec2-user/amazonses/bin/SES.pm /home/ec2-user/kittysearch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;FIXED! must move to the directory in the script using cd first to have access to SES.pm&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="ses-message-limit"&gt;SES Message Limit&lt;/h3&gt;
&lt;p&gt;Yyou can send 2,000 messages for free each day when you call Amazon SES from an Amazon EC2
instance directly or through AWS Elastic Beanstalk. (Note bandwidth charges may still apply)&lt;/p&gt;
&lt;h3 id="more-info"&gt;More info&lt;/h3&gt;
&lt;p&gt;Apparently since 2011 there has come along infrastructure like page2rss and ifttt that makes these kind of custom solutions less helpful (unless you need customization!)&lt;/p&gt;</content><category term="linux"/><category term="AWS"/><category term="EC2"/><category term="SES"/><category term="cron"/></entry><entry><title>Command Line DOS Networking</title><link href="https://blog.john-pfeiffer.com/command-line-dos-networking/" rel="alternate"/><published>2010-11-29T16:37:00-08:00</published><updated>2010-11-29T16:37:00-08:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2010-11-29:/command-line-dos-networking/</id><summary type="html">
&lt;p&gt;Disk Operating System is still quite useful even in Windows XP/2003/Vista/7 if you know the commands (and parameters).&lt;/p&gt;
&lt;h3 id="diagnostic-and-networking-commands"&gt;Diagnostic and Networking Commands&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;systeminfo.exe&lt;/code&gt; &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;uptime, OS, originall install, ram, domain, logonserver, nic's  &lt;/p&gt;
&lt;p&gt;(note that window98 had a gui, winipcfg from the Run prompt)  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;systeminfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;computername …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">
&lt;p&gt;Disk Operating System is still quite useful even in Windows XP/2003/Vista/7 if you know the commands (and parameters).&lt;/p&gt;
&lt;h3 id="diagnostic-and-networking-commands"&gt;Diagnostic and Networking Commands&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;systeminfo.exe&lt;/code&gt; &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;uptime, OS, originall install, ram, domain, logonserver, nic's  &lt;/p&gt;
&lt;p&gt;(note that window98 had a gui, winipcfg from the Run prompt)  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;systeminfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;computername&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;
&lt;span class="nx"&gt;ipconfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;
&lt;span class="nx"&gt;ipconfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;renew&lt;/span&gt;
&lt;span class="nx"&gt;ipconfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;flushdns&lt;/span&gt;
&lt;span class="nx"&gt;ping&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nx"&gt;tracert&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nx"&gt;pathping&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nx"&gt;netstat&lt;/span&gt;

&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="m m-Double"&gt;10.0.0.13&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="m m-Double"&gt;10.0.0.13&lt;/span&gt;
&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;delete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;delete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="m m-Double"&gt;10.0.0.13&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;share&lt;/span&gt;
&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="nx"&gt;fileservername&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;share&lt;/span&gt;
&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newpassword&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;

&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;localgroup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;administrators&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"domain users"&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;prompts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;these&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;member&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;workstation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;don&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;performed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SAM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SAM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Non&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;administrators&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;receive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"System error 5 has occurred. Access is denied"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;they&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;nbtstat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;127.0.0.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;nbtstat [-a RemoteName] [-A IPAddress] [-c] [-n] [-r] [-R] [-RR] [-s] [-S] [Interval]&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;-a RemoteName : Displays the NetBIOS name table of a remote computer, where RemoteName is the NetBIOS computer name of the remote computer.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The NetBIOS name table is the list of NetBIOS names that corresponds to NetBIOS applications running on that computer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-A IPAddress : Displays the NetBIOS name table of a remote computer, specified by the IP address (in dotted decimal notation) of the remote computer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-c : Displays the contents of the NetBIOS name cache, the table of NetBIOS names and their resolved IP addresses.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-n : Displays the NetBIOS name table of the local computer. 
The status of Registered indicates that the name is registered either by broadcast or with a WINS server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-r : Displays NetBIOS name resolution statistics. 
On a Windows XP computer that is configured to use WINS, this parameter returns the number of names that have been resolved and registered using broadcast and WINS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-R : Purges the contents of the NetBIOS name cache and then reloads the #PRE-tagged entries from the Lmhosts file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-RR : Releases and then refreshes NetBIOS names for the local computer that is registered with WINS servers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-s : Displays NetBIOS client and server sessions, attempting to convert the destination IP address to a name.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-S : Displays NetBIOS client and server sessions, listing the remote computers by destination IP address only.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Interval : Redisplays selected statistics, pausing the number of seconds specified in Interval between each display. Press CTRL+C to stop redisplaying statistics. If this parameter is omitted, nbtstat prints the current configuration information only once.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/nbtstat.mspx"&gt;http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/nbtstat.mspx&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="windows-logon-ids"&gt;Windows Logon Ids&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;2 interactive  &lt;/li&gt;
&lt;li&gt;3 network  &lt;/li&gt;
&lt;li&gt;4 batch  &lt;/li&gt;
&lt;li&gt;5 service  &lt;/li&gt;
&lt;li&gt;7 unlock  &lt;/li&gt;
&lt;li&gt;8 network cleartext  &lt;/li&gt;
&lt;li&gt;9 RunAs  &lt;/li&gt;
&lt;li&gt;10 RemoteInteractive  &lt;/li&gt;
&lt;li&gt;11 CachedInteractive&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="monitor-logon-script"&gt;Monitor Logon Script&lt;/h3&gt;
&lt;p&gt;Create your logon script and place it in the %SystemRoot%\System32 folder.  &lt;/p&gt;
&lt;p&gt;Run Regedt32.exe and go to the following value:  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion
\Winlogon\Appsetup&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After the last entry in the Appsetup value, place a comma and a space and then enter the name and extension of the logon script you placed in the %SystemRoot%\System32 folder. &lt;/p&gt;
&lt;p&gt;For example, if the value of Appsetup is:  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;Usrlogon.cmd, Rmvlinks.exe&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;After adding an entry for Termlogon.cmd, the value would look like:  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;Usrlogon.cmd, Rmvlinks.exe, Termlogon.cmd&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;echo %computername% %username% %date% %time% &amp;gt;&amp;gt; %homedrive%\%homepath%\log\log.txt&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="net-use"&gt;Net Use&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;net use /help&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;NET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;USE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;devicename | *&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;\\computername\sharename[\volume&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;password | *&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;/USER:[domainname\&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;/USER:[dotted domain name\&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;/USER:[username@dotted domain name&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;/SMARTCARD&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;/SAVECRED&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;[/DELETE&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;/PERSISTENT:{YES | NO}&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;NET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;USE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="n"&gt;devicename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;password | *&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;HOME&lt;/span&gt;

&lt;span class="n"&gt;NET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;USE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;/PERSISTENT:{YES | NO}&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;NET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;USE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connects&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;computer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;disconnects&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;computer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;When&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;without&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lists&lt;/span&gt;
&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;computer&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;devicename&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Assigns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;connect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;specifies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;disconnected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;There&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kinds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;
&lt;span class="nl"&gt;devicenames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;disk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;drives&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;D&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;through&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;printers&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;LPT1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;through&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;LPT3&lt;/span&gt;&lt;span class="p"&gt;:).&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;asterisk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instead&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;specific&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;devicename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;devicename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="n"&gt;computername&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;computer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;controlling&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;computername&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;contains&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;enclose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backslash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;computername&lt;/span&gt;
&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;quotation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;marks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;computername&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;may&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;characters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;long&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;sharename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Specifies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NetWare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;have&lt;/span&gt;
&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Netware&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Windows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Workstations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Gateway&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Netware&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Windows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;installed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;connect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NetWare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;needed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Produces&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;
&lt;span class="ow"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;displayed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Specifies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;different&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;made&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;domainname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Specifies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;another&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;domain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;omitted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logged&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;domain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Specifies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;SMARTCARD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Specifies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;smart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;SAVECRED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Specifies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ignored&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;unless&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;prompts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;HOME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Connects&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;their&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cancels&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;removes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;connection&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;persistent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;PERSISTENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Controls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;persistent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;YES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Saves&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;they&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;made&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;restores&lt;/span&gt;
&lt;span class="n"&gt;them&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="k"&gt;NO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Does&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;save&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;being&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;made&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subsequent&lt;/span&gt;
&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;restored&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;at&lt;/span&gt;
&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;persistent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;NET HELP command | MORE&lt;/code&gt; displays Help one screen at a time.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Net_use"&gt;http://en.wikipedia.org/wiki/Net_use&lt;/a&gt;
&lt;a href="http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/net_use.mspx"&gt;http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/net_use.mspx&lt;/a&gt;&lt;/p&gt;</content><category term="it"/><category term="DOS"/></entry><entry><title>Eclipse IDE C Wascana on Windows 7</title><link href="https://blog.john-pfeiffer.com/eclipse-ide-c-wascana-on-windows-7/" rel="alternate"/><published>2010-11-05T02:21:00-07:00</published><updated>2010-11-05T02:21:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2010-11-05:/eclipse-ide-c-wascana-on-windows-7/</id><summary type="html">
&lt;blockquote&gt;
&lt;p&gt;The Wascana IDE project was discontinued: &lt;a href="http://speedydeletion.wikia.com/wiki/Wascana_Desktop_Developer"&gt;http://speedydeletion.wikia.com/wiki/Wascana_Desktop_Developer&lt;/a&gt; which is probably why links no longer work&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Unfortunately I was trying to install the 64 bit version of everything below but could not find a reliable method of getting mingw 64 bit to work with Eclipse... &lt;/p&gt;
&lt;p&gt;See …&lt;/p&gt;</summary><content type="html">
&lt;blockquote&gt;
&lt;p&gt;The Wascana IDE project was discontinued: &lt;a href="http://speedydeletion.wikia.com/wiki/Wascana_Desktop_Developer"&gt;http://speedydeletion.wikia.com/wiki/Wascana_Desktop_Developer&lt;/a&gt; which is probably why links no longer work&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Unfortunately I was trying to install the 64 bit version of everything below but could not find a reliable method of getting mingw 64 bit to work with Eclipse... &lt;/p&gt;
&lt;p&gt;See the very end for the workaround. &lt;/p&gt;
&lt;p&gt;(Note: having a 64 bit compiler should theoretically compile faster but with gcc 32 bit you can compile / target both 32 and 64 applications.)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;install JRE (64 bit)&lt;/li&gt;
&lt;li&gt;install Eclipse CDT (64 bit)&lt;/li&gt;
&lt;li&gt;install Wascana (mingw for eclipse)&lt;/li&gt;
&lt;li&gt;configure the path variable&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="install-the-java-runtime-environment-jre"&gt;Install the Java Runtime Environment (JRE)&lt;/h3&gt;
&lt;p&gt;A pre-requisite is to download the JRE (Java Runtime Environment, 5.0 or higher, newer is often better).&lt;/p&gt;
&lt;p&gt;FIRST, check if you have a 64 bit or older 32 bit system.&lt;/p&gt;
&lt;p&gt;If you have a 64 bit system, use a "64 bit browser" to go to the java page because otherwise it will keep giving you the 32 bit version to download...&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.java.com/en/download/faq/java_win64bit.xml"&gt;http://www.java.com/en/download/faq/java_win64bit.xml&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The link and filename should be something like: "jre-6u22-windows-x64.exe"&lt;/p&gt;
&lt;p&gt;Otherwise in Eclipse you may get the Error exit code=13 or "failed to load the JNI shared library"...&lt;/p&gt;
&lt;p&gt;This will most likely install in C:\Program Files\Java (or some variant). (with the \bin\javaw.exe)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;on Windows 7 the C:\Program Files (x86)\ directory contains 32 bit installations/applications&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="install-eclipse-cdt-64-bit"&gt;Install Eclipse CDT (64 bit)&lt;/h3&gt;
&lt;p&gt;The base Eclipse CDT supports integration with the GNU toolchain but may not come with a compiler...&lt;/p&gt;
&lt;p&gt;All Linux distributions include the GNU toolchain (but might not be installed by default...)  &lt;/p&gt;
&lt;p&gt;MinGW provides the best integration support with the CDT due to it's direct support for the Windows environment.&lt;/p&gt;
&lt;p&gt;If you download the Eclipse IDE for C/C++ you'll get the "CDT" plugins along with the default Eclipse platform:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.eclipse.org/cdt/downloads.php"&gt;http://www.eclipse.org/cdt/downloads.php&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The eclipse.ini file allows you to configure your program (e.g. specify the JRE location)  &lt;/p&gt;
&lt;p&gt;(Notedpad2 or notepad++ handle the linux versus windows line breaks transparently...)  &lt;/p&gt;
&lt;p&gt;e.g. insert the line to specify where your java run time environment is, maybe you have two...&lt;/p&gt;
&lt;h4 id="eclipseini"&gt;eclipse.ini&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;-vm
C:\Java\jre6\bin\javaw.exe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Double clicking the eclipse.exe icon will start it with an empty Workbench (and use the default JRE)  &lt;/p&gt;
&lt;p&gt;The first time you will be given the opportunity to choose your "Workspace" (aka directory where all of your files will be stored).&lt;/p&gt;
&lt;p&gt;I prefer to have it in the Eclipse folder but obviously in a multi user setup the "My Docs" or Network Folder would also make sense... or Dropbox?   (DropBox -&amp;gt; Public might make Open Source Distribution even easier?)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;File -&amp;gt; New -&amp;gt; C Project&lt;/li&gt;
&lt;li&gt;Fill in the basics (you can choose the pre-made hello world app)&lt;/li&gt;
&lt;li&gt;Then click on the "Go To Workbench" so you can see the Project File Explorer, Code Editor, Console   &lt;blockquote&gt;
&lt;p&gt;Window -&amp;gt; Preferences allows you to customize Eclipse e.g. disable usage statistics)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Click on the hammer symbol (Build) to ensure that you create an object file (.o) before trying to test run an executable...&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="gcc-error"&gt;gcc error&lt;/h3&gt;
&lt;p&gt;Of course when you try to build you get an error...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Internal Builder: Cannot run program "gcc" (in directory "C:\My Dropbox\workspace\hello\Debug"): CreateProcess error=2, The system cannot find the file specified
Build error occurred, build is stopped
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Help -&amp;gt; Install New Software&lt;/strong&gt; (previously Software Installer)&lt;/p&gt;
&lt;p&gt;Work With gets pasted the URL of the Wascana C/C++ compiler for Eclipse, then click ADD&lt;br/&gt;
&lt;a href="http://svn.codespot.com/a/eclipselabs.org/wascana/repo"&gt;http://svn.codespot.com/a/eclipselabs.org/wascana/repo&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;seems to have moved to Google Code but does not allow access, maybe &lt;a href="http://sourceforge.net/projects/wascana/"&gt;http://sourceforge.net/projects/wascana/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Click on the Checkbox for "Wascana C/C++ Developer for Windows, then NEXT  &lt;/p&gt;
&lt;p&gt;(review items to be installed, e.g. wascana.core) NEXT, then Agree to the License Terms...&lt;/p&gt;
&lt;p&gt;After it downloads, installs, and restarts Eclipse you'll find the new mingw and msys directories in your Eclipse folder.&lt;/p&gt;
&lt;p&gt;Now you have to update the Path, in Windows it's usually under System Properties -&amp;gt; Advanced System Settings  &lt;/p&gt;
&lt;p&gt;Environment Variables -&amp;gt; System Variables scroll area, highlight "Path" (click on the edit button)&lt;/p&gt;
&lt;p&gt;It should already have something like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Append a semi colon to continue the long list and add:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;c:\eclipse\mingw\bin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Apparently some people feel eclipse does not autodetect unless it's c:\mingw&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(Theoretically you could also try installing MingW directly from their website, which I've done, but it again is not 64 bit).&lt;/p&gt;
&lt;h3 id="always-32-mingw-gcc-oops"&gt;Always 32 mingw gcc (oops)&lt;/h3&gt;
&lt;p&gt;Unfortunately fundamental flaw - even when using Eclipse 64 bit the
Install New Software gets the Wascana 32 bit mingw gcc.&lt;/p&gt;
&lt;p&gt;The Workaround is to use the Wascana Desktop as a single download/install (which includes 32 bit versions of: JRE 1.6.0 , Mingw 3.4.5 , Eclipse IDE)&lt;/p&gt;
&lt;h3 id="more-info"&gt;More info&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://code.google.com/a/eclipselabs.org/p/wascana/"&gt;http://code.google.com/a/eclipselabs.org/p/wascana/&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Apparently inaccessible due to strange Google Code permissions issues&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Also try: &lt;a href="http://mclserver.eecs.ucf.edu/trac/courses/wiki/COP3402Spring2011/InstallEclipseCpp"&gt;http://mclserver.eecs.ucf.edu/trac/courses/wiki/COP3402Spring2011/InstallEclipseCpp&lt;/a&gt;&lt;/p&gt;</content><category term="programming"/><category term="eclipse"/><category term="IDE"/><category term="c"/><category term="win7"/></entry><entry><title>Drupal Security Tip: disabling anonymous access to cron</title><link href="https://blog.john-pfeiffer.com/drupal-security-tip-disabling-anonymous-access-to-cron/" rel="alternate"/><published>2010-08-30T17:33:00-07:00</published><updated>2010-08-30T17:33:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2010-08-30:/drupal-security-tip-disabling-anonymous-access-to-cron/</id><summary type="html">
&lt;p&gt;Drupal is a wonderful way of leveraging many open source advanced web features into one interface that conceivably can be handed off to a "non developer" to maintain.&lt;/p&gt;
&lt;p&gt;Along with all of the installation / implementation (often customized to fit the customers' needs) there are two further things that should be …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Drupal is a wonderful way of leveraging many open source advanced web features into one interface that conceivably can be handed off to a "non developer" to maintain.&lt;/p&gt;
&lt;p&gt;Along with all of the installation / implementation (often customized to fit the customers' needs) there are two further things that should be considered, Security and Useability.&lt;/p&gt;
&lt;p&gt;Here's some tips on security and maintenance.&lt;/p&gt;
&lt;p&gt;Drupal is a Content Management System that allows remote users to run scripts and access databases on your web server, this is a &lt;strong&gt;serious responsibility&lt;/strong&gt; as Shared Hosting means your runaway/hacked scripts affects others, and Hackers/Spammers are always looking for new Zombies...&lt;/p&gt;
&lt;h3 id="restrict-access-to-php-scripts"&gt;Restrict access to PHP Scripts&lt;/h3&gt;
&lt;p&gt;Restrict the PHP scripts access from ANONYMOUS USERS ON THE INTERNET!&lt;/p&gt;
&lt;p&gt;"index.php" should be allowed (it's your home page) but...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cron is the method in linux to run scheduled tasks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Drupal requires regular scheduled actions for maintenance (e.g. update content in search, cleaning up log files, checking for updates, etc.)&lt;/p&gt;
&lt;p&gt;&lt;a href="http://drupal.org/cron.php"&gt;http://drupal.org/cron.php&lt;/a&gt; (should not be) accessible but &lt;a href="http://example.com/cron.php"&gt;http://example.com/cron.php&lt;/a&gt; may be accessible to ANYONE as that's the default install =( &lt;/p&gt;
&lt;p&gt;To secure the cron.php file in .htacess, you have to do lock it down manually after installation.&lt;/p&gt;
&lt;p&gt;To block remote access to cron.php, in the server's .htaccess file or vhost configuration file:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;.htaccess&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Deny&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;Allow&lt;/span&gt;
&lt;span class="nx"&gt;Deny&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;
&lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;localhost&lt;/span&gt;
&lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;127.0.0.1&lt;/span&gt;
&lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;xx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or protect update.php too in the .htaccess file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;deny&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;allow&lt;/span&gt;
&lt;span class="nx"&gt;deny&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;
&lt;span class="nx"&gt;allow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;127.0.0&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;allowed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;specific&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addresses&lt;/span&gt;
&lt;span class="nx"&gt;allow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;
&lt;span class="nx"&gt;allow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;NOW ANONYMOUS ACCESS TO CRON.PHP should return either "access denied" or "page not found"...&lt;/p&gt;
&lt;h3 id="running-drupal-cron-manually"&gt;Running Drupal cron manually&lt;/h3&gt;
&lt;p&gt;You can still run cron manually from either of the options below:  &lt;/p&gt;
&lt;p&gt;Administer -&amp;gt; Reports -&amp;gt; Status&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;http://example.com/admin/reports/status/run-cron&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There's even a way to schedule it to run against localhost or 127.0.0.1 (which is trusted in the .htaccess file we created above)&lt;/p&gt;
&lt;h3 id="cron-explained"&gt;Cron explained&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Cpanel -&amp;gt; Advanced -&amp;gt; Cron Jobs&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt; * &lt;span class="gs"&gt;* *&lt;/span&gt; * http://example.com/cron.php
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(e.g. for rochen or bluehost cpanelx command should be the 8 char directory)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;php -q /home/yoursite/public_html/cron.php
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;OR if you have multiple subdomains running different drupal installs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;php -q /home/8chars/public_html/subdomain/cron.php
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Check using your drupal admin to ensure that the cron job has run&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Administer -&amp;gt; Reports -&amp;gt; Status&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This will allow you to test if your cpanel really has the correct permissions as&lt;/p&gt;
&lt;p&gt;Administer -&amp;gt; Reports -&amp;gt; Status should now show the cron job status as updated frequently! =)&lt;/p&gt;
&lt;p&gt;Here is a diagram of the general crontab syntax, for illustration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# +---------------- minute (0 - 59)&lt;/span&gt;
&lt;span class="c1"&gt;# | +------------- hour (0 - 23)&lt;/span&gt;
&lt;span class="c1"&gt;# | | +---------- day of month (1 - 31)&lt;/span&gt;
&lt;span class="c1"&gt;# | | | +------- month (1 - 12)&lt;/span&gt;
&lt;span class="c1"&gt;# | | | | +---- day of week (0 - 7) (Sunday=0 or 7)&lt;/span&gt;
&lt;span class="c1"&gt;# | | | | |&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;executed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;e.g. 59 23 31 12 * /bin/execute/this/script.sh&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;the five stars (with a space in between each!) represent wildcards:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;when minute = 59&lt;/li&gt;
&lt;li&gt;when hour = 23&lt;/li&gt;
&lt;li&gt;when day = 31&lt;/li&gt;
&lt;li&gt;when month = 12&lt;/li&gt;
&lt;li&gt;every day (could be = 5 to limit it to only every friday)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="update-free-access-false"&gt;Update free access = FALSE&lt;/h3&gt;
&lt;p&gt;/sites/default/settings.php should definitely have:&lt;/p&gt;
&lt;p&gt;$update_free_access = FALSE;&lt;/p&gt;
&lt;h3 id="restricting-file-upload-extensions"&gt;Restricting file upload extensions&lt;/h3&gt;
&lt;p&gt;Administer -&amp;gt; Site configuration -&amp;gt; File uploads  &lt;/p&gt;
&lt;p&gt;"Default permitted file extensions field" for each role should be limited, because obviously you don't want ANONYMOUS users uploading .php files! &lt;/p&gt;
&lt;p&gt;(Or in INPUT FORMAT, .php code entered by an anonymous or hacked authenticated user!)&lt;/p&gt;</content><category term="it"/><category term="Drupal"/><category term="security"/></entry><entry><title>Drupal 6 wysiwyg module so users can insert images</title><link href="https://blog.john-pfeiffer.com/drupal-6-wysiwyg-module-so-users-can-insert-images/" rel="alternate"/><published>2010-08-26T05:00:00-07:00</published><updated>2010-08-26T05:00:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2010-08-26:/drupal-6-wysiwyg-module-so-users-can-insert-images/</id><summary type="html">
&lt;p&gt;Content manager made easy = WYSIWYG (what you see is what you get) (for Drupal 6)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.drupal.org/project/wysiwyg"&gt;https://www.drupal.org/project/wysiwyg&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="download-and-install-the-drupal-6-wysiwyg-module"&gt;Download and install the Drupal 6 WYSIWYG module&lt;/h3&gt;
&lt;p&gt;Download and install (aka get the .tgz and upload it to /sites/all/modules) and enable the module  (in the main …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Content manager made easy = WYSIWYG (what you see is what you get) (for Drupal 6)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.drupal.org/project/wysiwyg"&gt;https://www.drupal.org/project/wysiwyg&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="download-and-install-the-drupal-6-wysiwyg-module"&gt;Download and install the Drupal 6 WYSIWYG module&lt;/h3&gt;
&lt;p&gt;Download and install (aka get the .tgz and upload it to /sites/all/modules) and enable the module  (in the main Drupal Modules menu)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enable it with: Administer -&amp;gt; Site building -&amp;gt; Modules&lt;/li&gt;
&lt;li&gt;Administer -&amp;gt; Site configuration -&amp;gt; Wysiwyg&lt;/li&gt;
&lt;li&gt;lists which editor modules you can use (and a helpful download link)&lt;/li&gt;
&lt;li&gt;e.g. TinyMCE (Download) Not installed.&lt;/li&gt;
&lt;li&gt;Extract the archive and copy its contents into a new folder in the following location: &lt;strong&gt;sites/all/libraries/tinymce&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Administer -&amp;gt; Site configuration -&amp;gt; Input formats&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ensure that the "Content Manager Role" (or authenticated user?) has access to Full HTML&lt;br/&gt;
Also you can set the default format to Full HTML
(alternatively you can create a more limited input type role that matches your paranoia)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Administer -&amp;gt; Site configuration -&amp;gt; Wysiwyg&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Input Format = Full HTML = TinyMCE 3.8.8&lt;/li&gt;
&lt;li&gt;Edit the profile of your WYSIWYG editor to decide which buttons/functions are displayed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Up until now (by default) you only get bold and inserting images that are already uploaded...&lt;/p&gt;
&lt;h3 id="setting-up-imce-image-manager"&gt;Setting up IMCE (Image Manager)&lt;/h3&gt;
&lt;p&gt;IMCE (image manager) (with the WYSIWYG bridge module)&lt;/p&gt;
&lt;p&gt;Download and install (aka get the .tgz and upload it to /sites/all/modules) and enable the module  (in the main Drupal Modules menu)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Administer -&amp;gt; Site building -&amp;gt; Modules&lt;/li&gt;
&lt;li&gt;Administer -&amp;gt; Site configuration -&amp;gt; IMCE -&amp;gt; User-1  &lt;/li&gt;
&lt;li&gt;Configure any specific settings, then give a role (e.g. Content Manager) permission&lt;/li&gt;
&lt;li&gt;Administer -&amp;gt; Site configuration -&amp;gt; IMCE&lt;/li&gt;
&lt;li&gt;Enable the Image button (as IMCE is accessed from the Image plugin).&lt;/li&gt;
&lt;li&gt;Enable the IMCE plugin in the plugins/buttons configuration of the wysiwyg profiles of your choice. (checkbox)&lt;/li&gt;
&lt;li&gt;Edit the profile of your WYSIWYG editor to decide which buttons/functions are displayed.  &lt;blockquote&gt;
&lt;p&gt;You MUST include the checkbox IMCE...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="permissions"&gt;Permissions&lt;/h3&gt;
&lt;p&gt;One of the common gotchas in Drupal is forgetting to set permissions (and having to dig through a ton of UI to find them)&lt;/p&gt;
&lt;p&gt;Ensure the future "content manager" role has create content permissions  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Administer -&amp;gt; User management -&amp;gt; Roles = Add Role  &lt;/li&gt;
&lt;li&gt;Administer -&amp;gt; User management -&amp;gt;Permissions&lt;/li&gt;
&lt;li&gt;Then assign that role to the user&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="verify-it-all-works"&gt;Verify it all works&lt;/h3&gt;
&lt;p&gt;Finally a user with the appropriate role (e.g. "content manager" above) can insert bold/underline/etc.  and insert images (and upload photos using IMCE).&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log In&lt;/li&gt;
&lt;li&gt;Create a new article/post&lt;/li&gt;
&lt;li&gt;In the UI you should see the menu has a lot more buttons&lt;/li&gt;
&lt;/ol&gt;</content><category term="programming"/><category term="drupal"/><category term="wysiwyg"/></entry><entry><title>CSS 3 column liquid layout example</title><link href="https://blog.john-pfeiffer.com/css-3-column-liquid-layout-example/" rel="alternate"/><published>2010-08-15T14:14:00-07:00</published><updated>2010-08-15T14:14:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2010-08-15:/css-3-column-liquid-layout-example/</id><summary type="html">
&lt;p&gt;CSS is much better than HTML, but making a webpage look the way it ought to look can be very painful, frustrating, and time consuming.&lt;/p&gt;
&lt;p&gt;Not only do you have to create cross browser compatible code, but it has to look nice when you're done!&lt;/p&gt;
&lt;p&gt;This is just a basic …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;CSS is much better than HTML, but making a webpage look the way it ought to look can be very painful, frustrating, and time consuming.&lt;/p&gt;
&lt;p&gt;Not only do you have to create cross browser compatible code, but it has to look nice when you're done!&lt;/p&gt;
&lt;p&gt;This is just a basic example that you can experiment with and add to later, there are some comments but the code is mostly self explanatory:&lt;/p&gt;
&lt;h3 id="3-column-css-source-code"&gt;3 column css source code&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;xmlns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/1999/xhtml"&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt; &lt;span class="na"&gt;xml:lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/css"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


&lt;span class="c"&gt;/* required to kill off any extra "helpful" browser padding  */&lt;/span&gt;
&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="nn"&gt;container&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="nn"&gt;column1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c"&gt;/* full length column */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="nn"&gt;column2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c"&gt;/* wraps the div around the left of the prev object */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;yellow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="nn"&gt;column3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c"&gt;/* prevent the right column from being pushed down! */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;solid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"column1"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            left left left left left left left left left left left left
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"column2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                center center center
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;  
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"column3"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            right right right right right right right right right
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;!-- end div container --&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="3-column-css-example"&gt;3 column css example&lt;/h3&gt;
&lt;div class="field field-name-body field-type-text-with-summary field-label-hidden"&gt;
&lt;html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;head&gt;&lt;style type="text/css"&gt;/* required to kill off any extra "helpful" browser padding  */html, body {  margin: 0;   padding: 0;  height: 99%;} #container{    min-height: 100%;    height: 100%;    margin:0;     border: 1px solid green;}#column1 {    float: left;         width: 33%;  height: 100%;           /* full length column */ position: relative;   border: 1px solid red;}#column2 {  float: left;    /* wraps the div around the left of the prev object */   width: 34%;  height: 100%;    position: relative;   border: 1px solid yellow;}#column3 {   float: right;    width: 33%;  margin-left: -1%;   /* prevent the right column from being pushed down! */   height: 100%;    position: relative;   border: 1px solid blue;  overflow: hidden;}&lt;/style&gt;&lt;/head&gt;&lt;body&gt; &lt;div id="container"&gt; &lt;div id="column1"&gt;         left left left left left left left left left left left left      &lt;/div&gt; &lt;div id="column2"&gt;             center center center     &lt;/div&gt; &lt;div id="column3"&gt;         right right right right right right right right right        &lt;/div&gt;&lt;/div&gt;&lt;!-- end div container --&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;</content><category term="programming"/><category term="css"/></entry><entry><title>Tiny Core Linux with Linksys Wireless Card - no CD required installation</title><link href="https://blog.john-pfeiffer.com/tiny-core-linux-with-linksys-wireless-card-no-cd-required-installation/" rel="alternate"/><published>2010-08-04T01:52:00-07:00</published><updated>2010-08-04T01:52:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2010-08-04:/tiny-core-linux-with-linksys-wireless-card-no-cd-required-installation/</id><summary type="html">
&lt;p&gt;Tiny Core Linux is fast and works great... but it does take some effort to get setup... &lt;/p&gt;
&lt;p&gt;Here's how I got my Linksys wpc54g (v3) pci wireless card working with WPA - and I didn't burn a tiny core cd!&lt;/p&gt;
&lt;p&gt;You can't repartition a hard drive while actually using it so …&lt;/p&gt;</summary><content type="html">
&lt;p&gt;Tiny Core Linux is fast and works great... but it does take some effort to get setup... &lt;/p&gt;
&lt;p&gt;Here's how I got my Linksys wpc54g (v3) pci wireless card working with WPA - and I didn't burn a tiny core cd!&lt;/p&gt;
&lt;p&gt;You can't repartition a hard drive while actually using it so you'll most likely need GParted (ie from SystemRescueCD bootable cd) so that you can repartition / (root) and resize to have a spare linux partition... AND use:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mke2fs -t ext3 /dev/hda3&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(or wherever it is...)
hda3 = third partition on the first hard drive, you may need to use &lt;code&gt;fdisk -l&lt;/code&gt;
or Start -&amp;gt; Control Panel -&amp;gt; Administrative Tools -&amp;gt; Computer Management -&amp;gt; Disk Management&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;grub bootloader installed (preferrably to the MBR)  &lt;/li&gt;
&lt;li&gt;tinycore.iso (cd image of tiny core installation/live cd)  &lt;/li&gt;
&lt;li&gt;uniextract or isobuster (to open files from iso's)  &lt;/li&gt;
&lt;li&gt;ext2fsd (winxp application that allows copying files in/out of an ext2/ext3 partition)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;tcz&lt;/em&gt; files from an FTP repository listed in the section "Install on a Hard Drive Without Being Connected to the Internet" from  &lt;a href="http://wiki.tinycorelinux.net/wiki:start#installing"&gt;http://wiki.tinycorelinux.net/wiki:start#installing&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;wireless-2.6.29.1-tinycore.tcz  &lt;/li&gt;
&lt;li&gt;wireless_tools.tcz  &lt;/li&gt;
&lt;li&gt;wpa_supplicant.tcz  &lt;/li&gt;
&lt;li&gt;b43-fwcutter.tcz  &lt;/li&gt;
&lt;li&gt;open-ssl-0.9.8m.tcz&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="getting-the-pieces-ready"&gt;Getting the pieces ready&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Extract the files from the &lt;strong&gt;tinycore.iso&lt;/strong&gt; (using IsoBuster or UniExtract)...&lt;br/&gt;
OR if you have linux: &lt;code&gt;mount -o loop /path-to-iso/image-filename.iso /mnt/custom&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;We only need the &lt;strong&gt;bzImage&lt;/strong&gt; and &lt;strong&gt;tinycore.gz&lt;/strong&gt; files... &lt;blockquote&gt;
&lt;p&gt;CAPITAL I ON THE bzImage!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;USING Ext2 Volume Manager (ext2fsd) ... browse to your linux partition and create the following folder: &lt;strong&gt;/boot/tinycore&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Copy the "bzimage" and "tinycore.gz" files into the linux partition &lt;strong&gt;/boot/tinycore folder&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Also create the following text file: &lt;strong&gt;/tce/onboot.lst&lt;/strong&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;wireless&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;2.6&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;29.1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tinycore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tcz&lt;/span&gt;
&lt;span class="n"&gt;wireless_tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tcz&lt;/span&gt;
&lt;span class="n"&gt;b43&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;fwcutter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tcz&lt;/span&gt;
&lt;span class="n"&gt;openssl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tcz&lt;/span&gt;
&lt;span class="n"&gt;wpa_supplicant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tcz&lt;/span&gt;
&lt;span class="n"&gt;nano&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tcz&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;BE CAREFUL TO NOT HAVE ANY MISSPELLINGS OR EXTRA SPACES&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finally, create the directory /tce/optional and copy the above .tcz files into it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;TinyCore uses /tce/mydata.tgz to store your files in the /home and /opt directories. (Therefore you could sneak something in if you wanted to...?)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;ALSO, it uses .ashrc (e.g. not BASH command prompt) so any aliases are in /tce/mydata.tgz -&amp;gt; home/tc/.ashrc&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="modify-your-grub-legacy-menu"&gt;Modify your Grub (legacy) menu&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;menu.lst&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;title tinycore  
root (hd0,2)  
kernel /boot/tinycore/bzimage  
initrd /boot/tinycore/tinycore.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="oh-wait-wifi-drivers"&gt;Oh Wait, Wifi Drivers!&lt;/h3&gt;
&lt;p&gt;BUT my wifi depends on this Linksys wpc54g (v3) pci wireless card AND wpa encryption...&lt;/p&gt;
&lt;p&gt;So I've got &lt;strong&gt;wl_apsta.o&lt;/strong&gt; from my previous debian kernel 2.6.26 (with all of the linux-header and make and compiling commands to get that binary...)&lt;/p&gt;
&lt;p&gt;Without the correct fw5 (b43 firmware) &lt;code&gt;dmesg&lt;/code&gt; will contain:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;b43-phy0 ERROR: firmware file "b43/ucode5.fw"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;kernel firmware unhappy with wrong linksys driver&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;COPY &lt;strong&gt;wl_apsta.o&lt;/strong&gt; into &lt;strong&gt;/mnt/hda3/tce&lt;/strong&gt; (with EXT2FSD or usb stick or whatever)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Rather than hack into my-data.tgz we'll wait until we've booted into Tiny Core...&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="booting-into-tiny-core-linux"&gt;Booting into Tiny Core Linux&lt;/h3&gt;
&lt;p&gt;BOOTING INTO TINY CORE IS VERY FAST... (fingers crossed about everything before)&lt;/p&gt;
&lt;p&gt;Right Click -&amp;gt; Control Panel or a funny icon in the middle with screwdriver = Control Panel&lt;/p&gt;
&lt;p&gt;First check that our "onboot.lst" hack worked: Apps Audit -&amp;gt; OnBoot -&amp;gt; Maintenance&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(could also use: nano /mnt/hda3/tce/onboot.lst)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WHEN YOU CHOOSE TO SAVE/BACKUP&lt;/strong&gt; (or when prompted when closing to Save a Backup)  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;mydata.tgz&lt;/strong&gt; is created and it includes any modifications to &lt;strong&gt;/opt/bootlocal.sh&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;since things put in the &lt;strong&gt;bootlocal.sh&lt;/strong&gt; script are run as root... my wifi hack works...&lt;/p&gt;
&lt;p&gt;&lt;code&gt;nano bootlocal.sh&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir /lib/firmware  
b43-fwcutter -w /lib/firmware /mnt/hda3/tce/wl_apsta.o  
wpa_supplicant -B -iwlan0 -c/mnt/hda3/tce/wpa_supplicant.conf  
udhcpc -H hostname -b -i wlan0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;the /lib/firmware directory is necessary for the kernel to get the new drivers  &lt;/p&gt;
&lt;p&gt;the b43-fwcutter firmware cutter gets the drivers to the directory  &lt;/p&gt;
&lt;p&gt;wpa_supplicant starts in the background using wlan0 and the config file wpa_supplicant.conf&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wpa_passphrase ssid-network-name &amp;gt; wpa_supplicant.conf&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;prompts for the wireless network password, after you type it in press enter&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;udhcpc is busybox's dhcp client using "hostname", in the background on wlan0&lt;/p&gt;
&lt;p&gt;An alternative configuration in bootlocal.sh for a static ip would be...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ifconfig wlan0 10.0.0.99 netmask 255.255.255.0 up  
route add default gw 10.0.0.138  
echo "nameserver 10.0.0.138" &amp;gt; /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="verify-after-reboot"&gt;Verify after reboot&lt;/h3&gt;
&lt;p&gt;AFTER I REBOOTED ifconfig wlan0 //shows me my ip address  &lt;/p&gt;
&lt;p&gt;&lt;code&gt;ping 10.0.0.138&lt;/code&gt; and &lt;code&gt;ping http://kittyandbear.net&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ALL OK!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="adding-a-browser-to-tiny-core-linux"&gt;Adding a browser to Tiny Core Linux&lt;/h3&gt;
&lt;p&gt;Of course, now I have to install a browser...&lt;/p&gt;
&lt;p&gt;Since tinycore works from the core image and with then added modifications to be as lean and fast as possible you really need to explicitly choose what you want on your hard drive AND what you want started at boot time.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Right Click -&amp;gt; AppsBrowser or funny icon on the bottom right (gears)&lt;/li&gt;
&lt;li&gt;File -&amp;gt; Install Local Extension (anything on your hard drive but not in onboot.lst)  &lt;/li&gt;
&lt;li&gt;By default it lists your TCE/optional directory, double click on the one you want...
(If it isn't onboot and it isn't "installed" by above then it's not on your tinycore yet!)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;OR File -&amp;gt; AppsBrowser ... when you choose to install something from the "repository" be prepared to wait for about 5 minutes for it to load the hundreds of packages...&lt;/p&gt;
&lt;h3 id="miscellaneous-tiny-core-linux-notes"&gt;Miscellaneous Tiny Core Linux Notes&lt;/h3&gt;
&lt;p&gt;Right Click -&amp;gt; Shells -&amp;gt; Shell Dark or funny icon on the bottom left (terminal with prompt)&lt;/p&gt;
&lt;p&gt;Right Click -&amp;gt; Control Panel -&amp;gt; System Stats OR funny icon in the middle with screwdriver = Panel -&amp;gt; System Stats&lt;/p&gt;
&lt;p&gt;dmesg TAB shows you the b43 stuff  &lt;/p&gt;
&lt;p&gt;cpu is cpu type  &lt;/p&gt;
&lt;p&gt;mem is RAM free  &lt;/p&gt;
&lt;p&gt;net is network devices installed...&lt;/p&gt;
&lt;p&gt;loadkmap &amp;lt; /usr/share/kmap/uk.kmap&lt;/p&gt;</content><category term="linux"/><category term="linux"/><category term="tiny core linux"/></entry><entry><title>Electric Car Rebate... Why not go European?</title><link href="https://blog.john-pfeiffer.com/electric-car-rebate-why-not-go-european/" rel="alternate"/><published>2010-08-02T13:13:00-07:00</published><updated>2010-08-02T13:13:00-07:00</updated><author><name>john pfeiffer</name></author><id>tag:blog.john-pfeiffer.com,2010-08-02:/electric-car-rebate-why-not-go-european/</id><summary type="html">&lt;p&gt;I don't expect anybody to read this as Slashdot articles grow exponentially in comments each hour but...&lt;/p&gt;
&lt;p&gt;The price of petrol in London is about 116 pence per liter &lt;a href="http://www.whatprice.co.uk/petrol-prices"&gt;http://www.whatprice.co.uk/petrol-prices&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;1 US gallon = 3.78 liter&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;4.39 GBP (pounds) per gallon&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;1 pound = 1 …&lt;/code&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;I don't expect anybody to read this as Slashdot articles grow exponentially in comments each hour but...&lt;/p&gt;
&lt;p&gt;The price of petrol in London is about 116 pence per liter &lt;a href="http://www.whatprice.co.uk/petrol-prices"&gt;http://www.whatprice.co.uk/petrol-prices&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;1 US gallon = 3.78 liter&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;4.39 GBP (pounds) per gallon&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;1 pound = 1.5 dollars (exchange rates are always crazy)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;= 6.59 dollars per gallon&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;miles driven / mpg ... -&amp;gt; total cost of gasoline?  &lt;/p&gt;
&lt;p&gt;195k / 49.5 = 3939.40 * $6.59 = $25,960 (hybrid)  &lt;/p&gt;
&lt;p&gt;195k / 30 = 6500 * $6.59 = $42,835 (fuel efficient car)  &lt;/p&gt;
&lt;p&gt;195k / 15 = 13000 * $6.59 = $85,670 (normal/big car)&lt;/p&gt;
&lt;p&gt;So, while many will argue that "Europeans" are "controlling their oil consumption" through taxes, I would argue that the world has been susidizing the oil industry. Additionally, many American vehicles get 20 mpg or even 15 mpg.&lt;/p&gt;
&lt;p&gt;PLEASE REMEMBER, money is fiction (pieces of paper), work is economic fiction, government is fiction, and the price of Gas/Fossil Fuels is fiction. We all agree to a system but the system can and should be changed towards improvement.&lt;/p&gt;
&lt;p&gt;IEA: To promote efficiency, cut fossil fuel subsidies&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.cnet.com/news/iea-to-promote-efficiency-cut-fossil-fuel-subsidies/"&gt;http://www.cnet.com/news/iea-to-promote-efficiency-cut-fossil-fuel-subsidies/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.iea.org/files/energy_subsidies.pdf"&gt;http://www.iea.org/files/energy_subsidies.pdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.rita.dot.gov/bts/sites/rita.dot.gov.bts/files/publications/national_transportation_statistics/html/table_04_23.html"&gt;http://www.rita.dot.gov/bts/sites/rita.dot.gov.bts/files/publications/national_transportation_statistics/html/table_04_23.html&lt;/a&gt;
&lt;em&gt;Table 4-23: Average Fuel Efficiency of U.S. Light Duty Vehicles&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So perhaps instead of silly electric car rebates we should reduce Fossil Fuel susbidies and increase the tax on gas (yes, there are both Federal and State taxes on gas already so I'm not proposing some radical communist ideology), thereby making the market pay the actual costs (not even counting externalities like environmental impact!).&lt;/p&gt;</content><category term="it"/><category term="electric cars"/></entry></feed>