Gocharm

Implementing Juju charms in Go

Roger Peppe

Juju UI Engineering team.

What is Gocharm?

Composability

#!/bin/bash
set -eux

servicename=`config-get name`
cat > /etc/myservice-config << EOF
name=$servicename
EOF
restart myservice

Complexity escalation

Gocharm model

package mycharm

import "github.com/juju/gocharm/hook"

// RegisterHooks registers all hook functionality
// with the given registry. It returns immediately.
func RegisterHooks(r *hook.Registry) {
    r.RegisterHook("start", func() error {
        // Start first service here,
        return nil
    })
    r.RegisterHook("start", func() error {
        // Start second service here,
        return nil
    })
}

Gocharm metadata.yaml

name: mycharm
summary: A demonstration Go charm.
description: |
    A charm that does nothing but demonstrate Gocharm concepts.

Automatically generated

Building the charm

% cd $GOPATH/src/github.com/rogpeppe/gocharm-demo/mycharm-one
% gocharm -v
local:trusty/mycharm-one
% juju deploy local:trusty/gocharm gocharm

Acquiring context

type myCharm struct {
    ctxt *hook.Context
}

func RegisterHooks(r *hook.Registry) {
    var c myCharm
    r.RegisterContext(c.setContext, nil)
    r.RegisterHook("start", c.start)
}

func (c *myCharm) setContext(ctxt *hook.Context) error {
    c.ctxt = ctxt
    return nil   
}

func (c *myCharm) start() error {
    c.ctxt.SetStatus(hook.StatusActive, "happily idle")
    return nil
}

Acquiring context

type myCharm struct {
    ctxt *hook.Context
}

func RegisterHooks(r *hook.Registry) {
    var c myCharm
    r.RegisterContext(c.setContext, nil)
    r.RegisterHook("start", c.start)
}

func (c *myCharm) setContext(ctxt *hook.Context) error {
    c.ctxt = ctxt
    return nil
}

func (c *myCharm) start() error {
    c.ctxt.SetStatus(hook.StatusActive, "happily idle")
    return nil
}

Acquiring context

type myCharm struct {
    ctxt *hook.Context
}

func RegisterHooks(r *hook.Registry) {
    var c myCharm
    r.RegisterContext(c.setContext, nil)
    r.RegisterHook("start", c.start)
}

func (c *myCharm) setContext(ctxt *hook.Context) error {
    c.ctxt = ctxt
    return nil   
}

func (c *myCharm) start() error {
    c.ctxt.SetStatus(hook.StatusActive, "happily idle")
    return nil
}

Using the context.

type myCharm struct {
    ctxt *hook.Context
}

func RegisterHooks(r *hook.Registry) {
    var c myCharm
    r.RegisterContext(c.setContext, nil)
    r.RegisterHook("start", c.start)
}

func (c *myCharm) setContext(ctxt *hook.Context) error {
    c.ctxt = ctxt
    return nil   
}

func (c *myCharm) start() error {
    c.ctxt.SetStatus(hook.StatusActive, "happily idle")
    return nil
}

Adding a relation

func RegisterHooks(r *hook.Registry) {
    var c myCharm
    r.RegisterContext(c.setContext, nil)
    r.RegisterHook("start", c.start)
    c.http.Register(r.Clone("website"), "website", false)
    r.RegisterHook("*", c.changed)
}

func (c *myCharm) changed() error {
    if c.http.HTTPPort() == 0 {
        c.ctxt.SetStatus(hook.StatusWaiting, "waiting for HTTP server config")
    } else {
        c.ctxt.SetStatus(hook.StatusActive, fmt.Sprintf("active on port %d", c.http.HTTPPort()))
    }
    return nil
}

Running a web service

func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, Canonical")
}

Running a web service

type myCharm struct {
    ctxt *hook.Context
    svc httpservice.Service
}

func RegisterHooks(r *hook.Registry) {
    var c myCharm
    r.RegisterContext(c.setContext, nil)
    c.svc.Register(r.Clone("website"), "", "website", newHelloHandler)
    r.RegisterHook("*", c.changed)
}

func newHelloHandler(struct{}) (http.Handler, error) {
    return http.HandlerFunc(helloHandler), nil
}

func (c *myCharm) changed() error {
    c.svc.Start(struct{}{})
    if c.svc.ServiceStarted() {
        c.ctxt.SetStatus(hook.StatusActive, "web server active")
    } else {
        c.ctxt.SetStatus(hook.StatusWaiting, "web server inactive")
    }
    return nil
}

Other features

Future work

Caveats

Conclusion

go get github.com/juju/gocharm/...

Thank you

Roger Peppe

Juju UI Engineering team.