Skip to content

Commit

Permalink
Added comments
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Schaper committed May 27, 2019
1 parent 70628a8 commit 8e912fc
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 0 deletions.
6 changes: 6 additions & 0 deletions certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ const (
CertificatePath = "/certs"
)

// Certificate holds config a certificate
type Certificate struct {
Name string
Fs afero.Fs
}

// NewCertificate returns a new Certificate
func NewCertificate(name string, fs afero.Fs) *Certificate {
cert := &Certificate{
Name: name,
Expand All @@ -27,6 +29,7 @@ func NewCertificate(name string, fs afero.Fs) *Certificate {
return cert
}

// FindAllCertificates scans the /certs directory for .pem files and returns a collection of Certificates
func FindAllCertificates(fs afero.Fs) ([]*Certificate, error) {
var certs []*Certificate

Expand All @@ -49,6 +52,7 @@ func FindAllCertificates(fs afero.Fs) ([]*Certificate, error) {
return certs, nil
}

// VerifyCertificates returns an error if no valid certificates are found
func VerifyCertificates(fs afero.Fs) error {
certs, err := FindAllCertificates(fs)

Expand All @@ -63,6 +67,7 @@ func VerifyCertificates(fs afero.Fs) error {
return nil
}

// FindCertificateForHost returns the Certificate associated with the given hostname
func FindCertificateForHost(hostname string, fs afero.Fs) (*Certificate, error) {
certs, err := FindAllCertificates(fs)
if err != nil {
Expand All @@ -78,6 +83,7 @@ func FindCertificateForHost(hostname string, fs afero.Fs) (*Certificate, error)
return nil, fmt.Errorf("Unable to find certificate for %s", hostname)
}

// FullPath returns the full path of a certificate file
func (c *Certificate) FullPath() string {
return filepath.Join(CertificatePath, c.Name)
}
Expand Down
5 changes: 5 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ const (
APIVersion = "v1.22"
)

// Client holds an instance of the docker client
type Client struct {
DockerClient *client.Client
}

// NewClient returns a new Client or an error if not able to connect to the Docker daemon
func NewClient() (*Client, error) {
cli, err := client.NewClient(Socket, APIVersion, nil, nil)
if err != nil {
Expand All @@ -30,14 +32,17 @@ func NewClient() (*Client, error) {
return client, nil
}

// Events returns a channel of Docker events
func (c *Client) Events() (<-chan events.Message, <-chan error) {
return c.DockerClient.Events(context.Background(), types.EventsOptions{})
}

// ListContainers returns a collection of Docker containers
func (c *Client) ListContainers() ([]types.Container, error) {
return c.DockerClient.ContainerList(context.Background(), types.ContainerListOptions{})
}

// Inspect returns the full information for a container with the given container ID
func (c *Client) Inspect(id string) (types.ContainerJSON, error) {
return c.DockerClient.ContainerInspect(context.Background(), id)
}
3 changes: 3 additions & 0 deletions commander.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import (
"os/exec"
)

// Commander represents an interface for exec commands
type Commander interface {
Run(name string, arg ...string) ([]byte, error)
}

type Command struct{}

// Run executes a command returns the output
func (c Command) Run(name string, arg ...string) ([]byte, error) {
out, err := exec.Command(name, arg...).Output()
return out, err
Expand Down
16 changes: 16 additions & 0 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ const (
heraPort = "hera.port"
)

// A Handler is responsible for responding to container start and die events
type Handler struct {
Client *Client
}

// NewHandler returns a new Handler instance
func NewHandler(client *Client) *Handler {
handler := &Handler{
Client: client,
Expand All @@ -30,6 +32,7 @@ func NewHandler(client *Client) *Handler {
return handler
}

// HandleEvent dispatches an event to the appropriate handler method depending on its status
func (h *Handler) HandleEvent(event events.Message) {
switch status := event.Status; status {
case "start":
Expand All @@ -46,6 +49,8 @@ func (h *Handler) HandleEvent(event events.Message) {
}
}

// HandleContainer allows immediate tunnel creation when hera is started by treating existing
// containers as start events
func (h *Handler) HandleContainer(id string) error {
event := events.Message{
ID: id,
Expand All @@ -59,6 +64,8 @@ func (h *Handler) HandleContainer(id string) error {
return nil
}

// handleStartEvent inspects the container from a start event and creates a tunnel if the container
// has been appropriately labeled and a certificate exists for its hostname
func (h *Handler) handleStartEvent(event events.Message) error {
container, err := h.Client.Inspect(event.ID)
if err != nil {
Expand Down Expand Up @@ -95,6 +102,8 @@ func (h *Handler) handleStartEvent(event events.Message) error {
return nil
}

// handleDieEvent inspects the container from a die event and stops the tunnel if one exists.
// An error is returned if a tunnel cannot be found or if the tunnel fails to stop
func (h *Handler) handleDieEvent(event events.Message) error {
container, err := h.Client.Inspect(event.ID)
if err != nil {
Expand All @@ -119,6 +128,8 @@ func (h *Handler) handleDieEvent(event events.Message) error {
return nil
}

// resolveHostname returns the IP address of a container from its hostname.
// An error is returned if the hostname cannot be resolved after five attempts.
func (h *Handler) resolveHostname(container types.ContainerJSON) (string, error) {
var resolved []string
var err error
Expand All @@ -141,6 +152,7 @@ func (h *Handler) resolveHostname(container types.ContainerJSON) (string, error)
return "", fmt.Errorf("Unable to connect to %s", container.ID[:12])
}

// getLabel returns the label value from a given label name and container JSON.
func getLabel(name string, container types.ContainerJSON) string {
value, ok := container.Config.Labels[name]
if !ok {
Expand All @@ -150,6 +162,8 @@ func getLabel(name string, container types.ContainerJSON) string {
return value
}

// getCertificate returns a Certificate for a given hostname.
// An error is returned if the root hostname cannot be parsed or if the certificate cannot be found.
func getCertificate(hostname string) (*Certificate, error) {
rootHostname, err := getRootHostname(hostname)
if err != nil {
Expand All @@ -164,6 +178,8 @@ func getCertificate(hostname string) (*Certificate, error) {
return cert, nil
}

// getRootHostname parses and validates a URL and returns the root hostname (e.g.: domain.tld).
// An error is returned if the hostname does not contain a valid TLD.
func getRootHostname(hostname string) (string, error) {
httpsHostname := strings.Join([]string{"https://", hostname}, "")

Expand Down
4 changes: 4 additions & 0 deletions listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import (
"github.com/spf13/afero"
)

// Listener holds config for an event listener and is used to listen for container events
type Listener struct {
Client *Client
Fs afero.Fs
}

// NewListener returns a new Listener
func NewListener() (*Listener, error) {
client, err := NewClient()
if err != nil {
Expand All @@ -26,6 +28,7 @@ func NewListener() (*Listener, error) {
return listener, nil
}

// Revive revives tunnels for currently running containers
func (l *Listener) Revive() error {
handler := NewHandler(l.Client)
containers, err := l.Client.ListContainers()
Expand All @@ -43,6 +46,7 @@ func (l *Listener) Revive() error {
return nil
}

// Listen listens for container events to be handled
func (l *Listener) Listen() {
log.Info("Hera is listening")

Expand Down
16 changes: 16 additions & 0 deletions service.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ const (

var fs = afero.NewOsFs()

// Service holds config for an s6 service
type Service struct {
Hostname string
Commander
}

// NewService returns a new Service. Services are used to start and stop tunnel processes,
// as well as supervise processes to ensure they are kept alive.
func NewService(hostname string) *Service {
service := &Service{
Hostname: hostname,
Expand All @@ -29,28 +32,34 @@ func NewService(hostname string) *Service {
return service
}

// servicePath returns the full path for the service
func (s *Service) servicePath() string {
return filepath.Join(ServicesPath, s.Hostname)
}

// ConfigFilePath returns the full path for the service config file
func (s *Service) ConfigFilePath() string {
return filepath.Join(s.servicePath(), "config.yml")
}

// RunFilePath returns the full path for the service run command
func (s *Service) RunFilePath() string {
return filepath.Join(s.servicePath(), "run")
}

// supervisePath returns the full path for the service supervise command
func (s *Service) supervisePath() string {
return filepath.Join(s.servicePath(), "supervise")
}

// LogFilePath returns the full path for the service log file
func (s *Service) LogFilePath() string {
logPath := []string{filepath.Join(LogPath, s.Hostname), "log"}

return strings.Join(logPath, ".")
}

// Create creates a new service directory if one does not already exist
func (s *Service) Create() error {
exists, err := afero.DirExists(fs, s.servicePath())
if err != nil {
Expand All @@ -64,6 +73,7 @@ func (s *Service) Create() error {
return nil
}

// Supervise supervises a service
func (s *Service) Supervise() error {
_, err := s.Commander.Run("s6-svscanctl", "-a", ServicesPath)
if err != nil {
Expand All @@ -73,6 +83,7 @@ func (s *Service) Supervise() error {
return nil
}

// Start starts a service
func (s *Service) Start() error {
_, err := s.Commander.Run("s6-svc", "-u", s.servicePath())
if err != nil {
Expand All @@ -82,6 +93,7 @@ func (s *Service) Start() error {
return nil
}

// Stop stops a service
func (s *Service) Stop() error {
_, err := s.Commander.Run("s6-svc", "-d", s.servicePath())
if err != nil {
Expand All @@ -91,6 +103,7 @@ func (s *Service) Stop() error {
return nil
}

// Restart restarts a service
func (s *Service) Restart() error {
err := s.waitUntilDown()
if err != nil {
Expand All @@ -105,6 +118,7 @@ func (s *Service) Restart() error {
return nil
}

// waitUntilDown returns when the service is down
func (s *Service) waitUntilDown() error {
_, err := s.Commander.Run("s6-svwait", "-d", s.servicePath())
if err != nil {
Expand All @@ -114,6 +128,7 @@ func (s *Service) waitUntilDown() error {
return nil
}

// IsSupervised returns a bool to indicate if a service is supervised or not
func (s *Service) IsSupervised() (bool, error) {
registered, err := afero.DirExists(fs, s.supervisePath())
if err != nil {
Expand All @@ -123,6 +138,7 @@ func (s *Service) IsSupervised() (bool, error) {
return registered, nil
}

// IsRunning returns a bool to indicate if a service is running or not
func (s *Service) IsRunning() (bool, error) {
out, err := s.Commander.Run("s6-svstat", "-u", s.servicePath())
if err != nil {
Expand Down
11 changes: 11 additions & 0 deletions tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@ var (
registry = make(map[string]*Tunnel)
)

// Tunnel holds the corresponding config, certificate, and service for a tunnel
type Tunnel struct {
Config *Config
Certificate *Certificate
Service *Service
}

type Config struct {
// TunnelConfig holds the necessary configuration for a tunnel
IP string
Hostname string
Port string
}

func NewTunnel(config *Config, certificate *Certificate) *Tunnel {
// NewTunnel returns a Tunnel with its corresponding config and certificate
service := NewService(config.Hostname)

tunnel := &Tunnel{
Expand All @@ -36,6 +39,8 @@ func NewTunnel(config *Config, certificate *Certificate) *Tunnel {
return tunnel
}

// GetTunnelForHost returns the tunnel for a given hostname.
// An error is returned if a tunnel is not found.
func GetTunnelForHost(hostname string) (*Tunnel, error) {
tunnel, ok := registry[hostname]

Expand All @@ -46,6 +51,7 @@ func GetTunnelForHost(hostname string) (*Tunnel, error) {
return tunnel, nil
}

// Start starts a tunnel
func (t *Tunnel) Start() error {
err := t.prepareService()
if err != nil {
Expand All @@ -62,6 +68,7 @@ func (t *Tunnel) Start() error {
return nil
}

// Stop stops a tunnel
func (t *Tunnel) Stop() error {
log.Infof("Stopping tunnel %s", t.Config.Hostname)

Expand All @@ -73,6 +80,7 @@ func (t *Tunnel) Stop() error {
return nil
}

// prepareService creates the service and necessary files for the tunnel service
func (t *Tunnel) prepareService() error {
err := t.Service.Create()
if err != nil {
Expand All @@ -92,6 +100,7 @@ func (t *Tunnel) prepareService() error {
return nil
}

// startService starts the tunnel service
func (t *Tunnel) startService() error {
supervised, err := t.Service.IsSupervised()
if err != nil {
Expand Down Expand Up @@ -132,6 +141,7 @@ func (t *Tunnel) startService() error {
return nil
}

// writeConfigFile creates the config file for a tunnel
func (t *Tunnel) writeConfigFile() error {
configLines := []string{
"hostname: %s",
Expand All @@ -151,6 +161,7 @@ func (t *Tunnel) writeConfigFile() error {
return nil
}

// writeRunFile creates the run file for a tunnel
func (t *Tunnel) writeRunFile() error {
runLines := []string{
"#!/bin/sh",
Expand Down

0 comments on commit 8e912fc

Please sign in to comment.