{"data":{"site":{"siteMetadata":{"title":"IJHDev Blog","author":"Ian Hayward"}},"markdownRemark":{"id":"815cb5b9-4fba-51fd-99a3-a27d55dcc983","html":"<p>A guide to building a simple pipeline from PR to deploy with a scalable solution. (TurtleWare 2.0 pt3)</p>\n<!-- end -->\n<p>This is part 3 of a 3 part blog post about making v2 of turtle wear watch face. <a href=\"https://ijh.dev/middlewear-go\">You can check out part 1 here</a> and <a href=\"https://ijh.dev/making-wear-os\">you can check out part 2 here</a></p>\n<p>This post will also be covering everything from PR to releasing on a Kubernetes cluster. We will be skipping some of the basics of CI/CD pipelines and what they are, if you are not sure what that is <a href=\"https://ijh.dev/mobile-ci-cd/\">check out this blog post on a mobile CI/CD here</a>. </p>\n<h3>Git, pull requests and pipeline starting point</h3>\n<p><img src=\"./images/github.jpeg\" alt=\"GitHub\"></p>\n<p>A theme with this guide will be getting free stuff wherever able, the cool thing about open source projects is there is a lot of options. For a good solid Git repo with a good pull request system and plugins to use all these free tools, <a href=\"https://github.com\">you can’t go wrong with GitHub</a>. <a href=\"https://trunkbaseddevelopment.com\">Trunk based development</a> for git is still the preferred method for having something easy to admin but fast to release. For setting that kind of environment up on GitHub as well as protecting your open source project, make sure you have the following settings:</p>\n<h4><em>In options:</em></h4>\n<ul>\n<li>In Data services, turn on “Security Alerts” to find out if any third-party libraries have a vulnerability and need you to update</li>\n<li>In Merge button, turn “Allow squash merging” and “Automatically delete head branches” on and the others off. This will keep your Trunk/Master branch readable on what went out and when. This goes hand in hand with good pull request descriptions.</li>\n</ul>\n<h4><em>In branches:</em></h4>\n<ul>\n<li>Set your default branch to your Trunk/Master branch</li>\n<li>Create a new branch protection rule for the Trunk/Master</li>\n</ul>\n<h4><em>Inside the branch protection rule:</em></h4>\n<ul>\n<li>Turn on “Require pull request reviews before merging” with at least 1 approval needed. This will stop anyone from pushing code right into the master branch and instead need a code review before merging.</li>\n<li>With “Require pull request reviews before merging” the option to turn on “Dismiss stale pull request approval when new commits are pushed” appears. This will help if someone has already had their code reviewed but then adds the dreaded “just one more quick fix” to their PR from being able to merge the code without at least 1 reviewer re-checking the PR. </li>\n<li>Turn on “Require status checks to pass before merging”. This won’t have much of an effect yet but as we get further down we can come back to this rule and set up plugins to prevent PR’s from being merged before checks are complete.</li>\n</ul>\n<p>There are other things you can set up here for to depending on what you need such as Wikis and Social Preview images but you can play around there for whatever suits your project best. </p>\n<h3>SonarCloud</h3>\n<p><img src=\"./images/sonarcloud.jpeg\" alt=\"SonarCloud\"></p>\n<p>One important thing to add to your pipeline is static code analysis. This doesn’t replace the need for other engineers reviewing code, rather it acts as an extra reviewer who is obsessed with code rules. The tool we are going to use in this setup is <a href=\"https://sonarcloud.io/\">SonarCloud</a> as it’s free for open-source code and easily plugs into GitHub. You can sign up for a SonarCloud account by using your GitHub auth, this way you can point it to your project so it can analyze it in just a couple of steps. You can set the code analyser to fire every time you trigger a build and it will comment on the pull request. It will cover a range of rules from code duplication, vulnerabilities including <a href=\"https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project\">OWASP Top 10</a> bugs, and Unit test coverage. Going back to GitHub as well, we can now set the “Require status checks to pass before merging” to require SonarCloud to pass before allowing merging.</p>\n<h3>DockerHub</h3>\n<p><img src=\"./images/docker.jpeg\" alt=\"DockerHub\"></p>\n<p>So while there are more feature-rich automated building tools available, if your plan is to containerise your program <a href=\"https://www.docker.com/\">using Docker</a> then there is a simple automated builder in Docker <a href=\"https://hub.docker.com/\">called DockerHub</a>. Before you sign up for that it’s better if you set up a working docker file for your Go program.</p>\n<p>So for Turtle Wear the docker image is quite simple and looks like this:</p>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\">FROM golang<span class=\"token punctuation\">:</span>alpine AS builder\n\nRUN apk update &amp;&amp; apk add <span class=\"token punctuation\">-</span><span class=\"token punctuation\">-</span>no<span class=\"token punctuation\">-</span>cache git ca<span class=\"token punctuation\">-</span>certificates \n&amp;&amp; update<span class=\"token punctuation\">-</span>ca<span class=\"token punctuation\">-</span>certificates\nRUN adduser <span class=\"token punctuation\">-</span>D <span class=\"token punctuation\">-</span>g '' appuser\n\nWORKDIR $GOPATH/src/turtle<span class=\"token punctuation\">-</span>wear<span class=\"token punctuation\">-</span>api\n\nCOPY . .\n\nRUN go mod download &amp;&amp; go mod verify\nRUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go test <span class=\"token punctuation\">-</span>cover ./<span class=\"token punctuation\">...</span>\nRUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \ngo build <span class=\"token punctuation\">-</span>o /go/bin/turtle<span class=\"token punctuation\">-</span>wear<span class=\"token punctuation\">-</span>api ./cmd/turtle<span class=\"token punctuation\">-</span>functions/main.go\n\nFROM scratch\nCOPY <span class=\"token punctuation\">-</span><span class=\"token punctuation\">-</span>from=builder /etc/ssl/certs /etc/ssl/certs\nCOPY <span class=\"token punctuation\">-</span><span class=\"token punctuation\">-</span>from=builder /etc/passwd /etc/passwd\nCOPY <span class=\"token punctuation\">-</span><span class=\"token punctuation\">-</span>from=builder /go/bin/turtle<span class=\"token punctuation\">-</span>wear<span class=\"token punctuation\">-</span>api /go/bin/turtle<span class=\"token punctuation\">-</span>wear<span class=\"token punctuation\">-</span>api\nUSER appuser\nENTRYPOINT <span class=\"token punctuation\">[</span><span class=\"token string\">\"/go/bin/turtle-wear-api\"</span><span class=\"token punctuation\">]</span></code></pre></div>\n<p>Credit to <a href=\"https://github.com/ndane\">Nathan Dane</a> for showing me his implementation and working with me on modifying it for the project needs.</p>\n<p>With Golang you can get your unit tests running right inside the docker image as well. The line “RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go test -cover ./…” is what runs all the unit tests in the app and will fail the build if one doesn’t pass. Also the line “RUN apk update &#x26;&#x26; apk add —no-cache git ca-certificates &#x26;&#x26; update-ca-certificates” was needed otherwise the container couldn’t make external requests.</p>\n<p>Once you have a docker file setup and working, <a href=\"https://hub.docker.com/\">Login to DockerHub</a> and click “Create repository”. Add a name and a description, select public and then connect up your GitHub account and point it to your Golang build with the docker file. From here DockerHub will take the first build and if it’s all working put the image up ready from use. In the same way we did with SonarCloud we can go back to GitHub and set the “Require status checks to pass before merging” to require DockerHub to pass before allowing merging.</p>\n<h3>Kubernetes in Google Cloud Platform</h3>\n<p><img src=\"./images/kubernetes.jpeg\" alt=\"kubernetes\"></p>\n<p>“Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications. It groups containers that make up an application into logical units for easy management and discovery.” Is the business talk way of explaining what they are. In very layman’s terms, it’s a way to deploy containers that are self-sustaining, update automatically, and can grow and shrink as demand requires.</p>\n<p>There are a few different services to get Kubernetes up and running easily enough, AWS has EKS, Azure has AKS, IBM has… IBM Kubernetes, and Google have GKE. GKE is what we will be using for this guide as it’s the right balance between giving you control and doing things for you. Plus they give you $300 free credit to use in your first year so you can learn the system safely.</p>\n<p>To start with, create a new project on the Google Cloud Platform. To do this, first go to the sidebar, click “IAM &#x26; admin” then “Manage resources”. On this page click Create Project, then in the new project window that appears, enter a project name and other applicable details, then click create.</p>\n<p>With that setup, you can now navigate to the Kubernetes Engine in the same left sidebar and click on clusters. Click on create cluster and you should be brought to a new screen with a lot of options. First pay attention to the very left-hand side, here are some templates with descriptions to make choosing the right cluster easier. For the purpose of this guide, we will be using “Your first cluster” as it’s the cheapest and what we are looking for. If you are building something that has higher requirements then feel free to select a different template.</p>\n<p><img src=\"./images/templates.jpeg\" alt=\"templates\"></p>\n<p>After selecting your template you can move over to the next section to the right. Starting from the top we have Name which is simple enough. deally, remember to name it with “cluster” at the end as you will be naming a few things when setting this up and it’s good to keep a naming convention for clarity later on. Next is Location type, choose zonal unless you really need always-on coverage assurance. Zone or Region just allows you to select where your cluster is hosted. Everything else here is fine as standard for whatever template you’ve chosen, click create when you’re ready. Once that’s done you will be back on the Clusters screen, give your new cluster a few minutes to finish setting up before continuing on.</p>\n<p><img src=\"./images/clusternotready.jpeg\" alt=\"cluster not ready\"></p>\n<h5><em>Not ready yet</em></h5>\n<p><img src=\"./images/clusterready.jpeg\" alt=\"cluster ready\"></p>\n<h5><em>Green tick is good to go</em></h5>\n<p>Next, we need to deploy our container, so click Deploy at the top of the clusters screen. The screen shows Container first, we can take the image path from our DockerHub build, press done then continue. It then moves on to Configuration, here you can name the Application and Namespace, again name them “app” and “namespace” at the end to keep that clarity. At the bottom, it allows you to select your newly created cluster then click Deploy.</p>\n<p><img src=\"./images/deployingwait.jpeg\" alt=\"deploying wait times\"></p>\n<h5><em>More loading times</em></h5>\n<p>This will bring you inside the deployment details page, the next thing to do here is on a prompt near the top of the page which mentions exposing the service. If you click that you will go to another screen where you can map the port for inside the container and the outside world. I highly recommend keeping the port to 80 but using whatever port you have set up inside your code for the target port. Click Expose here and it will start opening up the port.</p>\n<p><img src=\"./images/exposeip.jpeg\" alt=\"Expose IP\">\nWith that all done you now have an External endpoint IP address. Use this to run tests on your code to make sure in the container is working correctly before moving on.</p>\n<h3>DNS Setup</h3>\n<p><img src=\"./images/dns.jpeg\" alt=\"DNS setup\"></p>\n<p>With the IP address from GKE all setup the pipeline is complete, but there is some quality of life things we can do before we call it a full success. Firstly having a raw IP address isn’t the nicest looking way to connect up an endpoint, you can very easily use a web domain you have to be your new endpoint name. If you go to your DNS records for that domain and do the following:</p>\n<ul>\n<li><em>Record type</em> : A</li>\n<li><em>Name</em> : Your domain name but with “api. before it</li>\n<li><em>Value</em> : This is your GKE ip address</li>\n</ul>\n<p>If you give it 5-10 mins for the DNS to propagate then give the same tests you were running on the IP address to the name in your DNS record you will find that everything works just the same but looks a lot nicer now.</p>\n<h3>Swagger docs</h3>\n<p><img src=\"./images/swagger.jpeg\" alt=\"Swagger\"></p>\n<p>Allowing anyone to understand how your endpoints work and allow them to test them out is a great way to help people on board and save you a lot of time explaining how it works. <a href=\"https://app.swaggerhub.com/\">Swagger Hub</a> offers a pretty good free hosted setup so long as again it’s open source. The site has an editor and a coding mode so you can set up the page, I would recommend looking at one of their demo pages to understand the layout properly. <a href=\"https://app.swaggerhub.com/apis-docs/ijhdev/turtle-wear-api/1.0.0\">You can check out the one I made for Turtle Wear API here</a></p>\n<h3>Wrapping up</h3>\n<p><img src=\"./images/wrapup.jpeg\" alt=\"The End\"></p>\n<p>There you go, you now have a pipeline so when you deploy new code to your Golang project it will check for code issues, run the unit tests, build into a container and then deploy to a scalable solution. This also wraps up the Turtle Wear trilogy of blog posts about my journey of doing a v2 of the app, hopefully it’s of some help to you!</p>\n<ul>\n<li><a href=\"https://github.com/seperot/turtle-wear-api\">Turtle Wear API</a></li>\n<li><a href=\"https://github.com/seperot/turtle-wear\">Turtle WearOS App</a></li>\n<li><a href=\"https://app.swaggerhub.com/apis-docs/ijhdev/turtle-wear-api/1.0.0\">Swagger</a></li>\n<li><a href=\"https://hub.docker.com/r/ijhdev/turtle-wear-api\">Docker</a></li>\n</ul>","frontmatter":{"title":"Golang automated pipeline","date":"December 09, 2019","featuredImage":{"childImageSharp":{"sizes":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAMF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAduZZQH/xAAWEAEBAQAAAAAAAAAAAAAAAAABABD/2gAIAQEAAQUChc//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAAAECL/2gAIAQEABj8CKX//xAAZEAACAwEAAAAAAAAAAAAAAAABEQAQIVH/2gAIAQEAAT8hmlAOIuv/2gAMAwEAAgADAAAAEHAf/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QZ//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAAMAAwEAAAAAAAAAAAAAAAABETFBgaH/2gAIAQEAAT8QdjkuqKelDmROqo//2Q==","aspectRatio":2.4518388791593697,"src":"/static/goline-62aadd8af557846018fcb4e9333040a7-6b3a1.jpg","srcSet":"/static/goline-62aadd8af557846018fcb4e9333040a7-182ff.jpg 213w,\n/static/goline-62aadd8af557846018fcb4e9333040a7-7f5c7.jpg 425w,\n/static/goline-62aadd8af557846018fcb4e9333040a7-6b3a1.jpg 850w,\n/static/goline-62aadd8af557846018fcb4e9333040a7-45cc1.jpg 1275w,\n/static/goline-62aadd8af557846018fcb4e9333040a7-055dc.jpg 1400w","sizes":"(max-width: 850px) 100vw, 850px"}}}}}},"pageContext":{"slug":"/open-source-pipeline/","previous":{"fields":{"slug":"/making-wear-os/"},"excerpt":"Making a wearOS watch face and avoiding some of the faff. (TurtleWare 2.0 pt2)\n","frontmatter":{"title":"Building a WearOS Watch face","date":"November 18, 2019","featuredImage":{"childImageSharp":{"sizes":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEE/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAL/2gAMAwEAAhADEAAAAcUKig//xAAYEAADAQEAAAAAAAAAAAAAAAAAARMREv/aAAgBAQABBQKaONJo/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAGRAAAgMBAAAAAAAAAAAAAAAAAREAECFB/9oACAEBAAE/IZAOEsq//9oADAMBAAIAAwAAABAAD//EABYRAAMAAAAAAAAAAAAAAAAAAAABEf/aAAgBAwEBPxCIiP/EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/EFf/xAAZEAACAwEAAAAAAAAAAAAAAAAAEQEhYVH/2gAIAQEAAT8Q0H8I23TQ/9k=","aspectRatio":2.4518388791593697,"src":"/static/trtlwearbig-040e897d594d2e7aa6b51466324174e4-6b3a1.jpg","srcSet":"/static/trtlwearbig-040e897d594d2e7aa6b51466324174e4-182ff.jpg 213w,\n/static/trtlwearbig-040e897d594d2e7aa6b51466324174e4-7f5c7.jpg 425w,\n/static/trtlwearbig-040e897d594d2e7aa6b51466324174e4-6b3a1.jpg 850w,\n/static/trtlwearbig-040e897d594d2e7aa6b51466324174e4-45cc1.jpg 1275w,\n/static/trtlwearbig-040e897d594d2e7aa6b51466324174e4-055dc.jpg 1400w","sizes":"(max-width: 850px) 100vw, 850px"}}}}},"next":{"fields":{"slug":"/golden-path-document/"},"excerpt":"A golden path document is the best way for new starters to come in strong and help those that come after them\n","frontmatter":{"title":"Setting up a Golden Path Document","date":"February 4, 2020","featuredImage":{"childImageSharp":{"sizes":{"base64":"data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAMEBf/EABYBAQEBAAAAAAAAAAAAAAAAAAIDBP/aAAwDAQACEAMQAAABr6RicAFP/8QAGBABAQADAAAAAAAAAAAAAAAAAQIAAyL/2gAIAQEAAQUCLzV1FiV//8QAFxEBAQEBAAAAAAAAAAAAAAAAAQACEf/aAAgBAwEBPwHK3W//xAAWEQEBAQAAAAAAAAAAAAAAAAABAhD/2gAIAQIBAT8BoM//xAAYEAEAAwEAAAAAAAAAAAAAAAAAAQIRIf/aAAgBAQAGPwJtocf/xAAYEAEBAQEBAAAAAAAAAAAAAAABABExQf/aAAgBAQABPyEBgeg5ZpaX/9oADAMBAAIAAwAAABAMP//EABYRAQEBAAAAAAAAAAAAAAAAAAABYf/aAAgBAwEBPxC5s//EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAgEBPxCZMf/EABoQAQADAAMAAAAAAAAAAAAAAAEAESExQXH/2gAIAQEAAT8Qxgr7O9BvhqM7CWW7P//Z","aspectRatio":2.4518388791593697,"src":"/static/goldenpathlarge-cee690aa1396307abec7f48cd3a95f08-6b3a1.jpg","srcSet":"/static/goldenpathlarge-cee690aa1396307abec7f48cd3a95f08-182ff.jpg 213w,\n/static/goldenpathlarge-cee690aa1396307abec7f48cd3a95f08-7f5c7.jpg 425w,\n/static/goldenpathlarge-cee690aa1396307abec7f48cd3a95f08-6b3a1.jpg 850w,\n/static/goldenpathlarge-cee690aa1396307abec7f48cd3a95f08-45cc1.jpg 1275w,\n/static/goldenpathlarge-cee690aa1396307abec7f48cd3a95f08-055dc.jpg 1400w","sizes":"(max-width: 850px) 100vw, 850px"}}}}}}}