github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/container/lxc/mock/mock-lxc.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package mock
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"sync"
    12  
    13  	"github.com/juju/loggo"
    14  	"github.com/juju/utils"
    15  	"launchpad.net/golxc"
    16  
    17  	"github.com/juju/juju/container"
    18  )
    19  
    20  // This file provides a mock implementation of the golxc interfaces
    21  // ContainerFactory and Container.
    22  
    23  var logger = loggo.GetLogger("juju.container.lxc.mock")
    24  
    25  type Action int
    26  
    27  const (
    28  	// A container has been started.
    29  	Started Action = iota
    30  	// A container has been stopped.
    31  	Stopped
    32  	// A container has been created.
    33  	Created
    34  	// A container has been destroyed.
    35  	Destroyed
    36  	// A container has been cloned.
    37  	Cloned
    38  )
    39  
    40  func (action Action) String() string {
    41  	switch action {
    42  	case Started:
    43  		return "Started"
    44  	case Stopped:
    45  		return "Stopped"
    46  	case Created:
    47  		return "Created"
    48  	case Destroyed:
    49  		return "Destroyed"
    50  	case Cloned:
    51  		return "Cloned"
    52  	}
    53  	return "unknown"
    54  }
    55  
    56  type Event struct {
    57  	Action       Action
    58  	InstanceId   string
    59  	Args         []string
    60  	TemplateArgs []string
    61  }
    62  
    63  type ContainerFactory interface {
    64  	golxc.ContainerFactory
    65  
    66  	AddListener(chan<- Event)
    67  	RemoveListener(chan<- Event)
    68  }
    69  
    70  type mockFactory struct {
    71  	containerDir string
    72  	instances    map[string]golxc.Container
    73  	listeners    []chan<- Event
    74  	mutex        sync.Mutex
    75  }
    76  
    77  func MockFactory(containerDir string) ContainerFactory {
    78  	return &mockFactory{
    79  		containerDir: containerDir,
    80  		instances:    make(map[string]golxc.Container),
    81  	}
    82  }
    83  
    84  type mockContainer struct {
    85  	factory  *mockFactory
    86  	name     string
    87  	state    golxc.State
    88  	logFile  string
    89  	logLevel golxc.LogLevel
    90  }
    91  
    92  func (mock *mockContainer) getState() golxc.State {
    93  	mock.factory.mutex.Lock()
    94  	defer mock.factory.mutex.Unlock()
    95  	return mock.state
    96  }
    97  
    98  func (mock *mockContainer) setState(newState golxc.State) {
    99  	mock.factory.mutex.Lock()
   100  	defer mock.factory.mutex.Unlock()
   101  	mock.state = newState
   102  	logger.Debugf("container %q state change to %s", mock.name, string(newState))
   103  }
   104  
   105  // Name returns the name of the container.
   106  func (mock *mockContainer) Name() string {
   107  	return mock.name
   108  }
   109  
   110  func (mock *mockContainer) configFilename() string {
   111  	return filepath.Join(mock.factory.containerDir, mock.name, "config")
   112  }
   113  
   114  // Create creates a new container based on the given template.
   115  func (mock *mockContainer) Create(configFile, template string, extraArgs []string, templateArgs []string) error {
   116  	if mock.getState() != golxc.StateUnknown {
   117  		return fmt.Errorf("container is already created")
   118  	}
   119  	mock.factory.instances[mock.name] = mock
   120  	// Create the container directory.
   121  	containerDir := filepath.Join(mock.factory.containerDir, mock.name)
   122  	if err := os.MkdirAll(containerDir, 0755); err != nil {
   123  		return err
   124  	}
   125  	if err := utils.CopyFile(mock.configFilename(), configFile); err != nil {
   126  		return err
   127  	}
   128  	mock.setState(golxc.StateStopped)
   129  	mock.factory.notify(eventArgs(Created, mock.name, extraArgs, templateArgs))
   130  	return nil
   131  }
   132  
   133  // Start runs the container as a daemon.
   134  func (mock *mockContainer) Start(configFile, consoleFile string) error {
   135  	state := mock.getState()
   136  	if state == golxc.StateUnknown {
   137  		return fmt.Errorf("container has not been created")
   138  	} else if state == golxc.StateRunning {
   139  		return fmt.Errorf("container is already running")
   140  	}
   141  	ioutil.WriteFile(
   142  		filepath.Join(container.ContainerDir, mock.name, "console.log"),
   143  		[]byte("fake console.log"), 0644)
   144  	mock.setState(golxc.StateRunning)
   145  	mock.factory.notify(event(Started, mock.name))
   146  	return nil
   147  }
   148  
   149  // Stop terminates the running container.
   150  func (mock *mockContainer) Stop() error {
   151  	state := mock.getState()
   152  	if state == golxc.StateUnknown {
   153  		return fmt.Errorf("container has not been created")
   154  	} else if state == golxc.StateStopped {
   155  		return fmt.Errorf("container is already stopped")
   156  	}
   157  	mock.setState(golxc.StateStopped)
   158  	mock.factory.notify(event(Stopped, mock.name))
   159  	return nil
   160  }
   161  
   162  // Clone creates a copy of the container, giving the copy the specified name.
   163  func (mock *mockContainer) Clone(name string, extraArgs []string, templateArgs []string) (golxc.Container, error) {
   164  	state := mock.getState()
   165  	if state == golxc.StateUnknown {
   166  		return nil, fmt.Errorf("container has not been created")
   167  	} else if state == golxc.StateRunning {
   168  		return nil, fmt.Errorf("container is running, clone not possible")
   169  	}
   170  
   171  	container := &mockContainer{
   172  		factory:  mock.factory,
   173  		name:     name,
   174  		state:    golxc.StateStopped,
   175  		logLevel: golxc.LogWarning,
   176  	}
   177  	mock.factory.instances[name] = container
   178  
   179  	// Create the container directory.
   180  	containerDir := filepath.Join(mock.factory.containerDir, name)
   181  	if err := os.MkdirAll(containerDir, 0755); err != nil {
   182  		return nil, err
   183  	}
   184  	if err := utils.CopyFile(container.configFilename(), mock.configFilename()); err != nil {
   185  		return nil, err
   186  	}
   187  
   188  	mock.factory.notify(eventArgs(Cloned, mock.name, extraArgs, templateArgs))
   189  	return container, nil
   190  }
   191  
   192  // Freeze freezes all the container's processes.
   193  func (mock *mockContainer) Freeze() error {
   194  	return nil
   195  }
   196  
   197  // Unfreeze thaws all frozen container's processes.
   198  func (mock *mockContainer) Unfreeze() error {
   199  	return nil
   200  }
   201  
   202  // Destroy stops and removes the container.
   203  func (mock *mockContainer) Destroy() error {
   204  	state := mock.getState()
   205  	// golxc destroy will stop the machine if it is running.
   206  	if state == golxc.StateRunning {
   207  		mock.Stop()
   208  	}
   209  	if state == golxc.StateUnknown {
   210  		return fmt.Errorf("container has not been created")
   211  	}
   212  	delete(mock.factory.instances, mock.name)
   213  	mock.setState(golxc.StateUnknown)
   214  	mock.factory.notify(event(Destroyed, mock.name))
   215  	return nil
   216  }
   217  
   218  // Wait waits for one of the specified container states.
   219  func (mock *mockContainer) Wait(states ...golxc.State) error {
   220  	return nil
   221  }
   222  
   223  // Info returns the status and the process id of the container.
   224  func (mock *mockContainer) Info() (golxc.State, int, error) {
   225  	pid := -1
   226  	state := mock.getState()
   227  	if state == golxc.StateRunning {
   228  		pid = 42
   229  	}
   230  	return state, pid, nil
   231  }
   232  
   233  // IsConstructed checks if the container image exists.
   234  func (mock *mockContainer) IsConstructed() bool {
   235  	return mock.getState() != golxc.StateUnknown
   236  }
   237  
   238  // IsRunning checks if the state of the container is 'RUNNING'.
   239  func (mock *mockContainer) IsRunning() bool {
   240  	return mock.getState() == golxc.StateRunning
   241  }
   242  
   243  // String returns information about the container, like the name, state,
   244  // and process id.
   245  func (mock *mockContainer) String() string {
   246  	state, pid, _ := mock.Info()
   247  	return fmt.Sprintf("<MockContainer %q, state: %s, pid %d>", mock.name, string(state), pid)
   248  }
   249  
   250  // LogFile returns the current filename used for the LogFile.
   251  func (mock *mockContainer) LogFile() string {
   252  	return mock.logFile
   253  }
   254  
   255  // LogLevel returns the current logging level (only used if the
   256  // LogFile is not "").
   257  func (mock *mockContainer) LogLevel() golxc.LogLevel {
   258  	return mock.logLevel
   259  }
   260  
   261  // SetLogFile sets both the LogFile and LogLevel.
   262  func (mock *mockContainer) SetLogFile(filename string, level golxc.LogLevel) {
   263  	mock.logFile = filename
   264  	mock.logLevel = level
   265  }
   266  
   267  func (mock *mockFactory) String() string {
   268  	return fmt.Sprintf("mock lxc factory")
   269  }
   270  
   271  func (mock *mockFactory) New(name string) golxc.Container {
   272  	mock.mutex.Lock()
   273  	defer mock.mutex.Unlock()
   274  	container, ok := mock.instances[name]
   275  	if ok {
   276  		return container
   277  	}
   278  	container = &mockContainer{
   279  		factory:  mock,
   280  		name:     name,
   281  		state:    golxc.StateUnknown,
   282  		logLevel: golxc.LogWarning,
   283  	}
   284  	mock.instances[name] = container
   285  	return container
   286  }
   287  
   288  func (mock *mockFactory) List() (result []golxc.Container, err error) {
   289  	for _, container := range mock.instances {
   290  		result = append(result, container)
   291  	}
   292  	return
   293  }
   294  
   295  func event(action Action, instanceId string) Event {
   296  	return Event{action, instanceId, nil, nil}
   297  }
   298  
   299  func eventArgs(action Action, instanceId string, args []string, template []string) Event {
   300  	return Event{action, instanceId, args, template}
   301  }
   302  
   303  func (mock *mockFactory) notify(event Event) {
   304  	for _, c := range mock.listeners {
   305  		c <- event
   306  	}
   307  }
   308  
   309  func (mock *mockFactory) AddListener(listener chan<- Event) {
   310  	mock.listeners = append(mock.listeners, listener)
   311  }
   312  
   313  func (mock *mockFactory) RemoveListener(listener chan<- Event) {
   314  	pos := 0
   315  	for i, c := range mock.listeners {
   316  		if c == listener {
   317  			pos = i
   318  		}
   319  	}
   320  	mock.listeners = append(mock.listeners[:pos], mock.listeners[pos+1:]...)
   321  }