<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by deeeet on Medium]]></title>
        <description><![CDATA[Stories by deeeet on Medium]]></description>
        <link>https://medium.com/@deeeet?source=rss-5665f2a6a9f5------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*y939zn_lqSXERani.jpeg</url>
            <title>Stories by deeeet on Medium</title>
            <link>https://medium.com/@deeeet?source=rss-5665f2a6a9f5------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 18 May 2026 02:46:50 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@deeeet/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Practices as a platform engineer (2020)]]></title>
            <link>https://medium.com/@deeeet/practices-as-a-platform-engineer-2020-200fe6c8e996?source=rss-5665f2a6a9f5------2</link>
            <guid isPermaLink="false">https://medium.com/p/200fe6c8e996</guid>
            <dc:creator><![CDATA[deeeet]]></dc:creator>
            <pubDate>Tue, 16 Jun 2020 00:54:49 GMT</pubDate>
            <atom:updated>2020-06-16T00:54:49.245Z</atom:updated>
            <content:encoded><![CDATA[<p>I’ve been working as a platform engineer (platformer) for almost 5 years. In the previous company, I worked on the internal PaaS based on <a href="https://www.cloudfoundry.org/">Cloud Foundry</a> and, in the current company, I’ve been working on the <a href="https://speakerdeck.com/tcnksm/microservices-platform-on-kubernetes-at-mercari">microservices platform</a> based on <a href="https://kubernetes.io/">Kubernetes</a>.</p><p>If you’ve done one thing for 5 years, you will have things that you take for granted as a matter of habit and become the basis of daily decision making, which is neutral from specific technologies. In this post, I wrote down these things as practices of platform engineers who build platform for developers. The things I listed below are not special. I think you’ve heard or seen these things somewhere. These are just results from daily inputs or experiences with the team I’ve been working.</p><p>Since they are just practices, you won’t be able to use them for 5 or 10 years and they should be updated when the trend or the world changes. This is just a record of 2020.</p><ul><li><strong>Immutable infrastructure</strong>: To provide a better product to customers faster, Continuous Delivery is one of the most important factors. A big part of making Continuous Delivery practice successful is to be able to push new changes without fear. The immutable infrastructure allows us to do deterministic deployment (safely rollouts and rollbacks). For successful continuous delivery and reliable delivery, all our components must be immutable. We don’t SSH to modify things in production. This practice must be also applied to configuration management or application source code as well.</li><li><strong>Environmental parity</strong>: We must keep the development and production of environmental parity as much as possible. We must enable developers (even us) to reproduce production issues in the development environment. Always test on development before production and grow confidence in production. And we must treat the development environment as production. If we break the development environment, it blocks the developer in testing and stalls agility.</li><li><strong>Declarative configuration</strong>: All infrastructure state or application state must be defined in “infrastructure as code” way. We should not do or depend on manual operations in production (and even in development). It leads to implicit knowledge and human errors. Infrastructure as code allows us to review the operation and automate it in CI. And the code must be declarative, not imperative. The declarative way enables us to build self-healing or autonomous components. It reduces our operation cost a lot. We should build tools based on “Infrastructure as data” way. We should not mix programmatic logic and data but clearly separate. The computation must be done by real programing language.</li><li><strong>Documentation as a product</strong>: Documentation is the minimum variable product of the platform. We must handle documentation as one of our products. This means the documentation must be always reviewed and maintained when something changes. When we release something new, we must provide the documentation together.</li><li><strong>Simple, consistent, and predictable</strong>: Developer experience (DX) we provide and operation workflows we prepare for our component should be simple, consistent, and predictable. How to work with component should be as simple to learn as possible and consistent across the multiple components (action to component A should be the same as component B). And action to the components should be always predictable. This means changes are always expected and visible before execution. The catastrophic changes should be always blocked or notified.</li><li><strong>Smart default but configurable</strong>: The tool we provide should be well abstracted and hide complex configuration (and underline complex infrastructure) as possible. Instead of asking developers to configure all but provide smart default which covers 80% of usage. To cover the rest of 20% of power users, open a well-designed control knob as well. We should consider both productivity and flexibility but the former should come first.</li><li><strong>Embrace failure</strong>: No software or infrastructure is perfect. Failures are always there. We should be embracing failures instead of focusing on preventing them. We must expect potential failures and actively be testing availability and reliability before going to production. Once we faced an incident, we must learn a lot from it and avoid the same failure again.</li><li><strong>Having a migration strategy, always</strong>: We don’t introduce new features without thinking about migration strategy from the existing ecosystem. Keeping multiple ways of doing the same things leads bad DX and becomes a burden for the operation (and even more we need to have a workaround for a legacy way to make it work with the new ecosystem). We don’t evaluate just introducing new features. Instead, we evaluate how much is used by developers (adaption rate). We don’t create a separate new platform for doing a new thing. There should be only one base platform.</li><li><strong>A platform for platforms</strong>: We can not support all workloads or requirements by a single platform. We must enable and help other teams to build their own specialized platform to support their own workflow top on our platform without involving the platform team.</li><li><strong>Operation by science, not by art</strong>: As a platform, we need to operate and maintain our components. The operation workflow or tool we design should be strongly backed by science and engineering, not art or individual knowledge or experience. We must handle it as a science. And we should avoid using all the time to react to the incident or manual operations. We must proactively be fixing the issue and the workflow and building tools. The time for proactive and reactive should be balanced well.</li><li><strong>Avoiding reinventing the wheel</strong>: Cloud providers provide great services (by using lots of costs and resources). The OSS ecosystem (especially cloud-native ecosystem) provides great software. We must utilize them first. We must be a good curator of them and utilize it for our platform. We must avoid reinventing someone else work (when reinventing we must balance the cost and benefit and resources we have). We must build something new, something special for our use cases, and something no one has worked before. And we must open to our jobs to the ecosystem as well.</li><li><strong>Standing on the shoulders of giants</strong>: As a microservices platform or microservices world, we are far behind from the giants. We must learn aggressively learn from what they succeeded or failed and the latest trends or research results (from conferences, papers, or meeting them). Our proposal and design must be based on these learnings. The originality must be on top of the existing knowledge. Then becoming the next giant and provide our knowledge to the world to support them.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=200fe6c8e996" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Writing an Interactive Message Bot for Slack in Golang]]></title>
            <link>https://medium.com/mercari-engineering/writing-an-interactive-message-bot-for-slack-in-golang-6337d04f36b9?source=rss-5665f2a6a9f5------2</link>
            <guid isPermaLink="false">https://medium.com/p/6337d04f36b9</guid>
            <category><![CDATA[chatops]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[slack]]></category>
            <category><![CDATA[sre]]></category>
            <dc:creator><![CDATA[deeeet]]></dc:creator>
            <pubDate>Mon, 04 Dec 2017 23:53:09 GMT</pubDate>
            <atom:updated>2017-12-05T04:12:00.108Z</atom:updated>
            <content:encoded><![CDATA[<p>Hey everyone, <a href="https://twitter.com/deeeet">@deeeet</a> here, from the Site Reliability Engineering (SRE) team.</p><p>At Mercari, we use Slack Bots to automate a lot of our tasks. For example, releases of our main API are automated with a bot, and over 10 releases per day occur on Slack across Mercari JP, US and UK.</p><p>At first, Slack Bots were mainly text-based, with the exception of being able to return an image, e.g., graph, as a response. However, bots have gotten much more useful since Slack started offering <a href="https://api.slack.com/interactive-messages">interactive messages</a>, allowing us to add dropdown menus, buttons, and more!</p><p>Buttons have actually been available for a while, but using them meant you had to open up your bot to other teams, and also support OAuth–basically, there were quite a few hurdles to overcome. But once Slack started offering <a href="https://api.slack.com/internal-integrations">internal integrations</a>, it became super simple to develop bots using interactive messages for use within your own team.</p><p>Mercari already uses multiple interactive Slack Bots, and we continue to develop more as new needs arise. In this article, I will explain how to write a bot with interactive messages. The sample code is all public (<a href="https://github.com/tcnksm/go-slack-interactive">tcnksm/go-slack-interactive</a>), so feel free to fork it and create a unique bot for yourself! (If you want to use Node, you might want to take a look at <a href="https://github.com/slackapi/sample-message-menus-node">Slack’s sample Node app</a>.)</p><h3>Benefits of Interactive Messages</h3><p>What’s so good about making a bot with interactive messages, you ask?? Because who doesn’t like clicking buttons!buttons are awesome!</p><p>With a single button, you can start a process and confirm that its requirements have been met. For really important processes, you can limit the number of people that can press the button and create an easy approval flow. If you use a menu, you can show the user a list of selections from the bot and get a preset response, which makes data validation easier compared to free-text input. Additionally, buttons are easier to use in the Slack mobile app.</p><p>It’s probably already obvious, but the main motivation behind making a bot is to collaborate with other team members in your channel. “If we’re all engineers, why not just use a command line tool?” you may ask, but our teammates are not all engineers. That’s what makes Slack bots great–they are accessible to everyone.</p><h3>An Example at Mercari</h3><p>At Mercari, we use interactive messages with a bot like the one below.</p><h4>Kubernetes Deploy Bot</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/604/0*uilgvm5Y08hgIeV_." /></figure><p>For Mercari US, we run some services on Kubernetes (<a href="https://cloud.google.com/kubernetes-engine/">GKE</a>). (We generally use Kubernetes when making new microservices, too.) The Kubernetes Deploy Bot deploys a newly made docker image to our production cluster. When the user asks the bot to deploy, the bot shows the user a list of deployable images (tags) in a menu, and the selected one is deployed to the cluster.</p><p>In addition to this, this bot also makes a pull request by rewriting the Docker image version in Kubernetes manifest file, so we have ensured that the repository setting file and the actual image version on cluster is same.</p><h4>Account Bot</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/606/0*6bVUYjz12FfeXzIl." /></figure><p>SRE are in charge of creating creating VPNs and accounts to access designated servers. In the past, the SRE were manually logging into the server, but this is now automated with a bot. When a user makes an account creation request, the bot requests approval from an SRE, who clicks the “accept” button. Thus, while it is automated after the approval, we ensure that the necessary approvals are in place before anyone creates an account.</p><p>By the way, this bot works on Google App Engine, and when it receives an account creation request, it submits a job to Google Cloud Pub Sub. Workers that actually create accounts are running in each of our three regions (JP, US and UK), and each subscribes to the appropriate topics for each region.</p><h3>Bot Example</h3><p>Below are instructions on how to make a bot with interactive messages, including some sample code.</p><p>As an example, we’ll create @beerbot in order to order beer. (Unfortunately, it doesn’t really order beer. :( ) When you chat with the bot, it shows a menu with a list of the beers that you can order. You can then use a button to confirm your order, or cancel. See how it works here:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/604/0*B3ydr7AGJyCGiXmB." /></figure><h3>Slack App Preparation</h3><p>Now we’ll get into actually making the bot. Interactive Message Bots need to be created as a Slack App. Firstly, we’ll <a href="https://api.slack.com/apps">make a new app</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*8EXs7ShW2hnEFY53." /></figure><p>Next, add a Bot user from the features panel and make Interactive Message active. At this point, it is necessary to set a specialized request URL. The results of a user’s action to an interactive message will be posted to this URL.</p><p>Lastly, we install the Slack app we made into the team Slack. From doing this, we can get a verification token to verify the bot’s User OAuth Access Token and Interactive Message request on the server side required for the bot to access the Slack API. This value is required to operate the Bot. With this, the preparation is finished.</p><h3>Bot Development with Golang</h3><p>Now, we will actually start writing the bot. It will need to</p><ul><li>Watch for Slack events and respond appropriately (Slack Event Listener)</li><li>Receive the results of a user’s interaction with our menu and buttons (Interactive Handler)</li></ul><h3>Slack Event Listener</h3><p>Firstly, we write the Slack Event Listener. In this example, it would respond to the message event “@beerbot hey” and show a menu to the user. For the Slack API Client, we use the <a href="https://github.com/nlopes/slack">nlopes/slack</a> package. We write the below listener and watch for the necessary MessageEvent.</p><pre>func (s *SlackListener) ListenAndResponse() { <br>    // Start listening slack events<br>    rtm := s.client.NewRTM()<br>    go rtm.ManageConnection()<br> <br>     // Handle slack events<br>     for msg := range rtm.IncomingEvents {<br>         switch ev := msg.Data.(type) {<br>         case *slack.MessageEvent:<br>             if err := s.handleMessageEvent(ev); err != nil {<br>                 log.Printf(“[ERROR] Failed to handle message: %s”, err)<br>             }<br>         }<br>     }<br>}</pre><p>Next, we’ll write the function below to process the MessageEvent.</p><pre>func (s *SlackListener) handleMessageEvent(ev *slack.MessageEvent) error</pre><p>In the function, firstly we validate the MessageEvent. At the very least, we need to confirm the following:</p><ul><li>Whether a message comes from expected channel (You must limit channel where bot work. For example, bots that do releases should only work in the release channel).</li><li>Whether the bot has been mentioned</li></ul><p>Next, the actual thing said to the bot (MessageEvent.Msg.Text) needs to be parsed. We can think of this in the same way as when we are writing a command line tool. The parsed message can inform the menu or other elements shown. (In this example, it just responds to “hey”.)</p><p>Next we actually show the menu. We will use the Attachment field of the <a href="https://api.slack.com/methods/chat.postMessage">Slack Post Message API</a> to show the menu. When written with Golang, it will be like the below.</p><pre>var attachment = slack.Attachment{<br>    Text: “Which beer do you want? :beer:”,<br>    Color: “#f9a41b”,<br>    CallbackID: “beer”,<br>    Actions: []slack.AttachmentAction{<br>        {<br>            Name: actionSelect,<br>            Type: “select”,<br>            Options: []slack.AttachmentActionOption{<br>                {<br>                    Text: “Asahi Super Dry”,<br>                    Value: “Asahi Super Dry”,<br>                },<br>                {<br>                    Text: “Kirin Lager Beer”,<br>                    Value: “Kirin Lager Beer”,<br>                },<br>                {<br>                    Text: “Sapporo Black Label”,<br>                    Value: “Sapporo Black Label”,<br>                },<br>                {<br>                    Text: “Suntory Malt’s”,<br>                    Value: “Suntory Malts”,<br>                },<br>                {<br>                    Text: “Yona Yona Ale”,<br>                    Value: “Yona Yona Ale”,<br>                 },<br>             },<br>         },<br>         {<br>              Name: actionCancel,<br>              Text: “Cancel”,<br>              Type: “button”,<br>              Style: “danger”,<br>         },<br>     },<br> }</pre><p>Firstly, we select either a menu (“select” type) or button for AttachmentAction.Type . AttachmentAction.Options slice will appear in the menu options (beer brands in this case). All that’s left to do is post this in the channel that received the event with the Slack Post API. After doing so, the menu will appear as below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*dXHTaErpIXzL2tp0." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*fc5V25Xd7NbDFoQy." /></figure><h3>Interactive Handler</h3><p>Next, we’ll write the handler that receives the results of the interactive message. See the example handler below:</p><pre>func (h interactionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {<br>    if r.Method != http.MethodPost {<br>        log.Printf(“[ERROR] Invalid method: %s”, r.Method)<br>        w.WriteHeader(http.StatusMethodNotAllowed)<br>        return<br>    }<br> <br>    buf, err := ioutil.ReadAll(r.Body)<br>    if err != nil {<br>        log.Printf(“[ERROR] Failed to read request body: %s”, err)<br>        w.WriteHeader(http.StatusInternalServerError)<br>        return<br>    }<br> <br>    jsonStr, err := url.QueryUnescape(string(buf)[8:])<br>    if err != nil {<br>        log.Printf(“[ERROR] Failed to unescape request body: %s”, err)<br>        w.WriteHeader(http.StatusInternalServerError)<br>        return<br>    }<br> <br>    var message slack.AttachmentActionCallback<br>    if err := json.Unmarshal([]byte(jsonStr), &amp;message); err != nil {<br>        log.Printf(“[ERROR] Failed to decode json message from slack: %s”, jsonStr)<br>        w.WriteHeader(http.StatusInternalServerError)<br>        return<br>     }<br> <br>    // Only accept message from slack with valid token<br>    if message.Token != h.verificationToken {<br>        log.Printf(“[ERROR] Invalid token: %s”, message.Token)<br>        w.WriteHeader(http.StatusUnauthorized)<br>        return<br>    }<br> ...<br>}</pre><p>The above examples are for the first half handler processing. (This will be the same for any interactive message) It is processing like the one shown below.</p><ul><li>Determining whether it is POST</li><li>Unescape the payload</li><li>Unmarshal the unescaped payload to slack.AttachmentActionCallback</li><li>Check that the token of the message received matches the verification token issued when you registered the Slack App.</li></ul><p>All we need to do now is to process the result of the user’s action. We’ll write this part like this;</p><pre>func (h interactionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {<br> ...<br>    action := message.Actions[0]<br>    switch action.Name {<br>    case actionSelect:<br>        value := action.SelectedOptions[0].Value<br>        // Overwrite original drop down message.<br>        originalMessage := message.OriginalMessage<br>        originalMessage.Attachments[0].Text = fmt.Sprintf(“OK to order %s ?”, strings.Title(value))<br>        originalMessage.Attachments[0].Actions = []slack.AttachmentAction{<br>            {<br>                Name: actionStart,<br>                Text: “Yes”,<br>                Type: “button”,<br>                Value: “start”,<br>                Style: “primary”,<br>            },<br>            {<br>                Name: actionCancel,<br>                Text: “No”,<br>                Type: “button”,<br>                Style: “danger”,<br>            },<br>        }<br>        w.Header().Add(“Content-type”, “application/json”)<br>        w.WriteHeader(http.StatusOK)<br>        json.NewEncoder(w).Encode(&amp;originalMessage)<br>        return<br>    case actionStart:<br>        title := “:ok: your order was submitted! yay!”<br>        responseMessage(w, message.OriginalMessage, title, “”)<br>        return<br>    case actionCancel:<br>        title := fmt.Sprintf(“:x: @%s canceled the request”, message.User.Name)<br>        responseMessage(w, message.OriginalMessage, title, “”)<br>        return<br>    default:<br>        log.Printf(“[ERROR] ]Invalid action was submitted: %s”, action.Name)<br>        w.WriteHeader(http.StatusInternalServerError)<br>        return<br>    }<br>}</pre><p>At this point, we will do processing for each action received. For dividing up the actions, we will use the definitions in AttachmentAction.Name. This time, we defined the below three actions.</p><ul><li>actionSelect — Selecting a menu option</li><li>actionStart — Confirming an order</li><li>actionCancel — Canceling an order</li></ul><p>For example, in the case of actionSelect, we determine their selection (beer brand) and confirm whether they really want to place an order with a button. In the event of actionCancel, the fact that the order was cancelled is returned as a response.</p><p>With Interactive Messages, the response to the user’s action overwrites their original message. This is to stop people from being able to press the button after they’ve sent the request. In order to do this, it is convenient to have the below function prepared. We delete the AttachmentAction of buttons etc. and return a response that overwrites the old message with a new one.</p><pre>func responseMessage(w http.ResponseWriter, original slack.Message, title, value string) {<br>    original.Attachments[0].Actions = []slack.AttachmentAction{} // empty buttons<br>    original.Attachments[0].Fields = []slack.AttachmentField{<br>        {<br>            Title: title,<br>            Value: value,<br>            Short: false,<br>        },<br>    }<br>    w.Header().Add(“Content-Type”, “application/json”)<br>    w.WriteHeader(http.StatusOK)<br>    json.NewEncoder(w).Encode(&amp;original)<br> }</pre><p>It is important to show who executed the action in the action response. If you don’t do this, you’ll lose track of who initiated the action in the first place. With this bot, it has been made to allow people to know who pressed the button. (For help designing interactive buttons, see <a href="https://api.slack.com/docs/message-guidelines">guidelines for building messages</a>.)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*vdB2pvq2e_dNbIFX." /></figure><h3>Conclusion</h3><p>In this article, I explained how to develop and write a Slack bot with interactive messages in Golang. I hope it helps you to create easy-to-use bots with interactive messages to automate time-consuming tasks! At Mercari, we are always looking for SREs who love automation and Golang. See our <a href="https://www.mercari.com/careers/">hiring page</a> for more details!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6337d04f36b9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mercari-engineering/writing-an-interactive-message-bot-for-slack-in-golang-6337d04f36b9">Writing an Interactive Message Bot for Slack in Golang</a> was originally published in <a href="https://medium.com/mercari-engineering">Making Mercari</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Draft on Google Container Engine]]></title>
            <link>https://medium.com/@deeeet/draft-on-google-container-engine-f806aa42875c?source=rss-5665f2a6a9f5------2</link>
            <guid isPermaLink="false">https://medium.com/p/f806aa42875c</guid>
            <category><![CDATA[kubernetes]]></category>
            <dc:creator><![CDATA[deeeet]]></dc:creator>
            <pubDate>Thu, 01 Jun 2017 06:21:42 GMT</pubDate>
            <atom:updated>2017-06-04T18:37:49.489Z</atom:updated>
            <content:encoded><![CDATA[<p>At CoreOS Fest 2017, Microsoft Azure team <a href="https://azure.microsoft.com/en-us/blog/streamlining-kubernetes-development-with-draft/">announced</a> <a href="https://github.com/azure/draft">Draft</a>, a tool that streamlines application development and deployment into k8s cluster. It’s really cool to me. So I tried to install it on my test GKE cluster.</p><p>Installation of Draft is quite easy (see <a href="https://github.com/Azure/draft/blob/master/docs/install.md">documentation</a>) and it’s done less than 10 min. But there are some missing parts for doing it on Google Container Engine (GKE) with Google Container Registry. Here is small notes.</p><p>(If you don’t have) Setup GKE cluster,</p><pre>$ gcloud container clusters create ${CLUSTER} \<br>        --project ${PROJECT_ID} \<br>        --zone ${ZONE}</pre><p>Draft uses a wildcard domain. Since this is just trial, it’s too much to prepare the domain. So in this time, just using <a href="http://xip.io/">xip.io</a>. After <a href="https://github.com/Azure/draft/blob/master/docs/ingress.md">you’ve installed the nginx-ingress controller</a>, you can get an external IP address of it. If it’s, for example, 35.181.245.100, you can use <a href="http://35.181.245.100.xip.io">35.181.245.100.xip.io</a> for your Draft wildcard domain.</p><p>Draft needs to push docker images to a container registry. On GKE, it’s easy to use Google Container Registry. To do it, you need some auth token setup.</p><p>First, get the access token,</p><pre> $ gcloud auth application-default print-access-token<br>yn89.GlxcBB8ylkd...</pre><p>Then, format &amp; base64 encode the token,</p><pre>$ echo &#39;{&quot;registrytoken&quot;:&quot;yn89.GlxcBB8ylkd...&quot;}&#39; | base64 -w 0<br>eyJyZWdpc3RyeXRva2VuIjoieW44OS5HbH...</pre><p>Now preparation is done. Run the following command and install Draft!</p><pre>$ draft init --set registry.url=gcr.io,registry.org=${PROJECT_ID},registry.authtoken=eyJyZWdpc3RyeXRva2VuIjo,basedomain=<a href="http://35.181.245.100.xip.io">35.181.245.100.xip.io</a></pre><p>That’s it. Now you can start using Draft! Easy! Yay!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f806aa42875c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Tracing HTTP request latency in golang]]></title>
            <link>https://medium.com/@deeeet/trancing-http-request-latency-in-golang-65b2463f548c?source=rss-5665f2a6a9f5------2</link>
            <guid isPermaLink="false">https://medium.com/p/65b2463f548c</guid>
            <category><![CDATA[api]]></category>
            <category><![CDATA[golang]]></category>
            <dc:creator><![CDATA[deeeet]]></dc:creator>
            <pubDate>Tue, 27 Sep 2016 09:13:25 GMT</pubDate>
            <atom:updated>2016-09-27T13:37:50.091Z</atom:updated>
            <content:encoded><![CDATA[<h4>httpstat as a golang package</h4><p><a href="https://github.com/reorx/httpstat">reorx/httpstat</a> ( and followed <a href="https://twitter.com/davecheney">@davecheney</a>’s <a href="https://github.com/davecheney/httpstat">golang implementation</a> ) is getting hot recently ( there is also <a href="https://github.com/yosuke-furukawa/httpstat">node implementation </a>!) . It’s very useful to see these latency with intuitive way in console when something wrong happens on network.</p><p>While contributing the project, I thought it’s very useful if I can output the same information on my own golang HTTP client ( or my network connected CLI tool ) when debugging. So I wrote a small package to do this, <a href="https://github.com/tcnksm/go-httpstat">https://github.com/tcnksm/go-httpstat</a> .</p><p>Because it creates a <a href="https://golang.org/pkg/net/http/httptrace/">httptrace</a> powered `context.Context`, you just need to pass it to your `http.Request` by `withContext()` method ( So must be less code modification !).</p><p>The following is an example usage of this package (trace request to <a href="https://github.com">https://github.com</a> ).</p><pre>// Create a new HTTP request<br>req, err := http.NewRequest(&quot;GET&quot;, &quot;https://github.com&quot;, nil)<br>if err != nil {<br>    log.Fatal(err)<br>}</pre><pre>// Create a httpstat powered context<br>var result httpstat.Result<br>ctx := httpstat.WithHTTPStat(req.Context(), &amp;result)<br>req = req.WithContext(ctx)</pre><pre>// Send request by default HTTP client<br>client := http.DefaultClient<br>res, err := client.Do(req)<br>if err != nil {<br>    log.Fatal(err)<br>}</pre><pre>if _, err := io.Copy(ioutil.Discard, res.Body); err != nil {<br>    log.Fatal(err)<br>}<br>res.Body.Close()<br>end := time.Now()</pre><pre>// Show the results<br>log.Printf(&quot;DNS lookup: %d ms&quot;, int(result.DNSLookup/time.Millisecond))<br>log.Printf(&quot;TCP connection: %d ms&quot;, int(result.TCPConnection/time.Millisecond))<br>log.Printf(&quot;TLS handshake: %d ms&quot;, int(result.TLSHandshake/time.Millisecond))<br>log.Printf(&quot;Server processing: %d ms&quot;, int(result.ServerProcessing/time.Millisecond))<br>log.Printf(&quot;Content transfer: %d ms&quot;, int(result.ContentTransfer(time.Now())/time.Millisecond))</pre><p>The result of this is like below,</p><pre>2016/09/27 17:58:38 DNS lookup: 5 ms<br>2016/09/27 17:58:38 TCP connection: 180 ms<br>2016/09/27 17:58:38 TLS handshake: 461 ms<br>2016/09/27 17:58:38 Server processing: 296 ms<br>2016/09/27 17:58:38 Content transfer: 180 ms</pre><p>The results are pretty much same as `httpstat` command. But there are some concerns about TLS handshaking time (see <a href="https://github.com/davecheney/httpstat/pull/7">#7</a>). Be care to use this in production as for now. I’ll continue to improve this.</p><p>If you find a something, please file a issue on <a href="https://github.com/tcnksm/go-httpstat">Github</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=65b2463f548c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Subtests & Sub-benchmarks in Go1.7]]></title>
            <link>https://medium.com/@deeeet/subtests-sub-benchmark-in-go1-7-6ede57b66297?source=rss-5665f2a6a9f5------2</link>
            <guid isPermaLink="false">https://medium.com/p/6ede57b66297</guid>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[deeeet]]></dc:creator>
            <pubDate>Tue, 09 Aug 2016 10:26:34 GMT</pubDate>
            <atom:updated>2016-08-09T16:41:46.511Z</atom:updated>
            <content:encoded><![CDATA[<h4>Table-driven benchmarking</h4><p>In <a href="https://tip.golang.org/doc/go1.7">Go1.7</a>, `testing` package will supports <a href="https://tip.golang.org/doc/go1.7#testing">subtests and sub-benchmarks</a>. With this feature, you can define tests/benchmarks in a test/benchmark function. It’s easy to create hierarchical tests or table-driven sub-benchmarks. It also provides a way to share common setup and tear-down code.</p><p>This feature may have less benefits on tests because normally <a href="https://github.com/golang/go/wiki/TableDrivenTests">table-driven tests</a> is enough. It will improve your benchmarks codes a lot.</p><p>Let me show some sample codes. For example, when you want to take benchmark of function `Foo` with different configuration, before Go1.7, you need to prepare functions for each configuration.</p><pre>// you need to prepare functions for each bench mark setting<br>func BenchmarkFoo1(b *testing.B) { benchFoo(b, 1) }<br>func BenchmarkFoo10(b *testing.B) { benchFoo(b, 10) }<br>func BenchmarkFoo100(b *testing.B) { benchFoo(b, 100) }</pre><pre>// helper function to run Foo with different config<br>func benchFoo(b *testing.B, config int) {<br> for i := 0; i &lt; b.N; i++ {<br> Foo(base)<br>}</pre><p>If you use Sub-benchmarks feature in Go1.7, you can write this benchmark like the following.</p><pre>func BenchmarkFoo(b *testing.B) {<br>    cases := []struct {<br>         Config int<br>    }{<br>         {Config: 1},<br>         {Config: 10},<br>         {Config: 100},<br>    }</pre><pre>    for _, bc := range cases {<br>        b.Run(fmt.Sprintf(“%d”, bc.Config), func(b *testing.B) { benchFoo(b, bc.Base) })<br>    }<br>}</pre><p>You can use table-driven approach! It’s simple and easy to read (Benchmark name will be Top level function name + first argument of `Run` method. e.g., `BenchmarkFoo/1`, `BenchmarkFoo/2`… )</p><p>If you check standard library changes in Go1.7, you can see it benefits from sub-benchmarks a lot. The followings are some of examples,</p><ul><li><a href="https://go-review.googlesource.com/#/c/23428/">https://go-review.googlesource.com/#/c/23428/</a></li><li><a href="https://go-review.googlesource.com/#/c/23429/">https://go-review.googlesource.com/#/c/23429/</a></li><li><a href="https://go-review.googlesource.com/#/c/23420/">https://go-review.googlesource.com/#/c/23420/</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6ede57b66297" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building private Docker registry with basic authentication]]></title>
            <link>https://medium.com/@deeeet/building-private-docker-registry-with-basic-authentication-with-self-signed-certificate-using-it-e6329085e612?source=rss-5665f2a6a9f5------2</link>
            <guid isPermaLink="false">https://medium.com/p/e6329085e612</guid>
            <dc:creator><![CDATA[deeeet]]></dc:creator>
            <pubDate>Fri, 03 Oct 2014 07:18:17 GMT</pubDate>
            <atom:updated>2014-10-03T07:52:36.083Z</atom:updated>
            <content:encoded><![CDATA[<h4>by self-signed certificate, using it from OSX</h4><p>If we can not use DockerHub (Public registry), we need to build private one. Docker provide Docker image for private registry, so it’s very easy to start to it.</p><pre>$ docker run -p 5000:5000 registry<br>$ docker push docker-private.com:5000/test-image:latest </pre><p>Easy, it’s done ! But it’s dangerous. Anyone who knows registry URL can push their own Docker images. So we need some authentication. Generally, we use Basic authentication because Docker client (docker login command) has function for it. Docker registry itself doesn’t have auth function, so we need to setup nginx or Appache in front of Docker registry as reverse proxy for Basic authentication.</p><p>In this case, we have some constrain (But, its reasonable):</p><ul><li>To Basic authentication, docker client ask us to use SSL</li><li>Docker client verify secure certificate and we can’t allow insecure</li></ul><p>If we use self-signed certification (for lazy), its’s a little hard and complicated steps. So I write procedures. For environment, we use Ubuntu for server, nginx for reverse proxy, client for OSX + boot2docker.</p><h3>Server-side settings</h3><p>We need 3 settings:</p><ul><li>nginx</li><li>Password for Basic authentication</li><li>Serf-signed certification</li></ul><h4>nginx</h4><p>For reverse proxy, we use nginx. Docker registry provides example of nginx configuration in its repository (<a href="https://github.com/docker/docker-registry/tree/master/contrib/nginx">https://github.com/docker/docker-registry/tree/master/contrib/nginx</a>). So we use it directly.</p><pre>$ git clone <a href="https://github.com/docker/docker-registry">https://github.com/docker/docker-registry</a> <br>$ cp docker-registry/contrib/nginx/nginx_1–3–9.conf /etc/nginx/conf.d/. <br>$ cp docker-registry/contrib/nginx/docker-registry.conf /etc/nginx/.</pre><h4>password</h4><p>We need to setup user and password for basic authentication.</p><pre>$ htpasswd -bc /etc/nginx/docker-registry.htpasswd USERNAME PASSWORD </pre><h4>Serf-signed certification</h4><p>First, we need to initialise CA serial file and generate CA private and public keys:</p><pre>$ echo 01 &gt; ca.srl <br>$ openssl genrsa -des3 -out ca-key.pem 2048 <br>$ openssl req -new -x509 -days 365 -key ca-key.pem -out ca.pem </pre><p>Now we have a CA, you can create a server key and certificate signing request (CSR).</p><pre>$ openssl genrsa -des3 -out server-key.pem 2048 <br>$ openssl req -subj ‘/CN=&lt;Your Hostname Here&gt;’ -new -key server-key.pem -out server.csr <br>$ openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem -out server-cert.pem </pre><p>We should remove the passphrase from server key:</p><pre>$ openssl rsa -in server-key.pem -out server-key.pem </pre><p>Finally, we install server-key and server-cert:</p><pre>$ cp server-cert.pem /etc/ssl/certs/docker-registry <br>$ cp server-key.pem /etc/ssl/private/docker-registry </pre><h3>Client-side settings</h3><p>Docker client verify secure certificate and we can’t allow insecure connection like cURL’s -k option. So we need to make our CA valid one (There are some Issues to allow insecure connection, but not realized. See more <a href="https://github.com/docker/docker/pull/2687">#2687</a>, <a href="https://github.com/docker/docker/pull/5817">#5817</a> ).</p><p>If you use boot2docker on OSX, <strong>you need to setup it not on OSX but on boot2docker-vm. </strong>Here, we use CA public key which we generate above step<strong> (</strong><a href="https://github.com/boot2docker/boot2docker/issues/347">https://github.com/boot2docker/boot2docker/issues/347</a>).</p><pre>$ boot2docker ssh<br>$ cat ca.pem &gt;&gt; /etc/ssl/certs/ca-certificates.crt<br>$ /etc/init.d/docker restart</pre><p>Thats all, now we can login to our private registry.</p><pre>$ docker login https://docker-private.com</pre><h4>References</h4><ul><li><a href="https://docs.docker.com/articles/https/">Running Docker with https</a></li><li><a href="http://www.activestate.com/blog/2014/01/deploying-your-own-private-docker-registry">Deploying your own Private Docker Registry | ActiveState</a></li><li><a href="https://github.com/docker/docker/pull/2687#issuecomment-50266315">https://github.com/docker/docker/pull/2687#issuecomment-50266315</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e6329085e612" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>