tableflip: github.com/cloudflare/tableflip/testing Index | Examples | Files

package testing

import "github.com/cloudflare/tableflip/testing"

Package testing provides a stub implementation that can be used for simplified testing of applications that normally use tableflip. It is also helpful for allowing projects that use tableflip able to run on Windows, which does not support tableflip.

This shows how to use the upgrader with the graceful shutdown facilities of net/http and using the stub implementation if on an unsupported platform.

Code:

package main

import (
    "context"
    "errors"
    "flag"
    "fmt"
    "log"
    "net"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/cloudflare/tableflip"
    "github.com/cloudflare/tableflip/testing"
)

type upgrader interface {
    Listen(network, addr string) (net.Listener, error)
    Stop()
    Upgrade() error
    Ready() error
    Exit() <-chan struct{}
}

// This shows how to use the upgrader
// with the graceful shutdown facilities of net/http
// and using the stub implementation if on an unsupported platform.
func main() {
    var (
        listenAddr = flag.String("listen", "localhost:8080", "`Address` to listen on")
        pidFile    = flag.String("pid-file", "", "`Path` to pid file")
    )

    flag.Parse()
    log.SetPrefix(fmt.Sprintf("%d ", os.Getpid()))

    var upg upgrader
    upg, err := tableflip.New(tableflip.Options{
        PIDFile: *pidFile,
    })
    if errors.Is(err, tableflip.ErrNotSupported) {
        upg, _ = testing.New()
    } else if err != nil {
        panic(err)
    }
    defer upg.Stop()

    // Do an upgrade on SIGHUP
    // NOTE: With `testing.Upgrader` this goroutine is useless
    // You may choose to enclose it inside an `if` statement block.
    go func() {
        sig := make(chan os.Signal, 1)
        signal.Notify(sig, syscall.SIGHUP)
        for range sig {
            err := upg.Upgrade()
            if err != nil {
                log.Println("Upgrade failed:", err)
            }
        }
    }()

    // Listen must be called before Ready
    ln, err := upg.Listen("tcp", *listenAddr)
    if err != nil {
        log.Fatalln("Can't listen:", err)
    }

    server := http.Server{
        // Set timeouts, etc.
    }

    go func() {
        err := server.Serve(ln)
        if err != http.ErrServerClosed {
            log.Println("HTTP server:", err)
        }
    }()

    log.Printf("ready")
    if err := upg.Ready(); err != nil {
        panic(err)
    }
    <-upg.Exit()

    // Make sure to set a deadline on exiting the process
    // after upg.Exit() is closed. No new upgrades can be
    // performed if the parent doesn't exit.
    time.AfterFunc(30*time.Second, func() {
        log.Println("Graceful shutdown timed out")
        os.Exit(1)
    })

    // Wait for connections to drain.
    server.Shutdown(context.Background())
}

Index

Examples

Package Files

fds.go upgrader.go

type Fds Uses

type Fds struct{}

func (*Fds) AddConn Uses

func (f *Fds) AddConn(network, addr string, conn net.Conn) error

AddConn does nothing, since there is no reason to track connections in the stub implementation

func (*Fds) AddFile Uses

func (f *Fds) AddFile(name string, file *os.File) error

AddFile does nothing, since there is no reason to track connections in the stub implementation

func (*Fds) AddListener Uses

func (f *Fds) AddListener(network, addr string, ln net.Listener) error

AddListener does nothing, since there is no reason to track connections in the stub implementation

func (*Fds) Conn Uses

func (f *Fds) Conn(network, addr string) (net.Conn, error)

Conn always returns nil, since it is impossible to inherit with the stub implementation

func (*Fds) File Uses

func (f *Fds) File(name string) (*os.File, error)

File always returns nil, since it is impossible to inherit with the stub implementation

func (*Fds) Listen Uses

func (f *Fds) Listen(network, addr string) (net.Listener, error)

Listen returns a listener by calling net.Listen directly

Note: In the stub implementation, this is the only function that actually does anything

func (*Fds) Listener Uses

func (f *Fds) Listener(network, addr string) (net.Listener, error)

Listener always returns nil, since it is impossible to inherit with the stub implementation

type Upgrader Uses

type Upgrader struct {
    *Fds
}

Upgrader has all the methods of tableflip.Upgrader, but they don't actually do anything special.

func New Uses

func New() (*Upgrader, error)

New creates a new stub Upgrader.

Unlike the real version, this can be called many times.

func (*Upgrader) Exit Uses

func (u *Upgrader) Exit() <-chan struct{}

Exit returns a channel which is closed when the process should exit. We can return nil here because reading from a nil channel blocks

func (*Upgrader) HasParent Uses

func (u *Upgrader) HasParent() bool

HasParent is always false, since the stub implementation can never have a parent

func (*Upgrader) Ready Uses

func (u *Upgrader) Ready() error

Ready does nothing, since it is impossible to inherit with the stub implementation. However, the function still needs to be callable without errors in order to be useful.

func (*Upgrader) Stop Uses

func (u *Upgrader) Stop()

Stop does nothing, since there will never be anything to stop in the stub implementation

func (*Upgrader) Upgrade Uses

func (u *Upgrader) Upgrade() error

Upgrade always returns an error in the stub implementation, since nothing can be done.

func (*Upgrader) WaitForParent Uses

func (u *Upgrader) WaitForParent(ctx context.Context) error

WaitForParent returns immediately, since the stub implementation can never be a parent

Package testing imports 4 packages (graph). Updated 2020-06-09. Refresh now. Tools for package owners.