testing

package
v1.2.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 30, 2022 License: BSD-3-Clause Imports: 4 Imported by: 0

Documentation

Overview

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.

Example (HttpShutdown)

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.

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())
}
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Fds

type Fds struct{}

func (*Fds) AddConn

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

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

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

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

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

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

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

type Upgrader struct {
	*Fds
}

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

func New

func New() (*Upgrader, error)

New creates a new stub Upgrader.

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

func (*Upgrader) Exit

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

func (u *Upgrader) HasParent() bool

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

func (*Upgrader) Ready

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

func (u *Upgrader) Stop()

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

func (*Upgrader) Upgrade

func (u *Upgrader) Upgrade() error

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

func (*Upgrader) WaitForParent

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

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL