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 }