github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/container/kvm/mock/mock-kvm.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  	"sync"
     9  
    10  	"github.com/juju/juju/container/kvm"
    11  )
    12  
    13  // This file provides a mock implementation of the kvm interfaces
    14  // ContainerFactory and Container.
    15  
    16  type Action int
    17  
    18  const (
    19  	// A container has been started.
    20  	Started Action = iota
    21  	// A container has been stopped.
    22  	Stopped
    23  )
    24  
    25  func (action Action) String() string {
    26  	switch action {
    27  	case Started:
    28  		return "Started"
    29  	case Stopped:
    30  		return "Stopped"
    31  	}
    32  	return "unknown"
    33  }
    34  
    35  type Event struct {
    36  	Action     Action
    37  	InstanceId string
    38  }
    39  
    40  type ContainerFactory interface {
    41  	kvm.ContainerFactory
    42  
    43  	AddListener(chan<- Event)
    44  	RemoveListener(chan<- Event)
    45  	HasListener(chan<- Event) bool
    46  }
    47  
    48  type mockFactory struct {
    49  	instances map[string]kvm.Container
    50  	listeners []chan<- Event
    51  	mu        sync.Mutex
    52  }
    53  
    54  func MockFactory() ContainerFactory {
    55  	return &mockFactory{
    56  		instances: make(map[string]kvm.Container),
    57  	}
    58  }
    59  
    60  type MockContainer struct {
    61  	StartParams kvm.StartParams
    62  
    63  	factory *mockFactory
    64  	name    string
    65  	started bool
    66  }
    67  
    68  // Name returns the name of the container.
    69  func (mock *MockContainer) Name() string {
    70  	return mock.name
    71  }
    72  
    73  // imageCacheCalls does nothing more than get incremented by calls to the
    74  // mocked EnsureCachedImage method below.
    75  // However, it will cause the race checker to fail if such calls are not made
    76  // in a Goroutine safe manner.
    77  var imageCacheCalls int //nolint:unused
    78  
    79  // EnsureCachedImage is the first supply of start-params to the container.
    80  // We set it here for subsequent test assertions.
    81  // Start is called by the manager immediately after, with the same argument.
    82  func (mock *MockContainer) EnsureCachedImage(params kvm.StartParams) error {
    83  	imageCacheCalls++
    84  	mock.StartParams = params
    85  	return nil
    86  }
    87  
    88  func (mock *MockContainer) Start(params kvm.StartParams) error {
    89  	if mock.started {
    90  		return fmt.Errorf("container is already running")
    91  	}
    92  	mock.started = true
    93  	mock.factory.notify(Started, mock.name)
    94  	return nil
    95  }
    96  
    97  // Stop terminates the running container.
    98  func (mock *MockContainer) Stop() error {
    99  	if !mock.started {
   100  		return fmt.Errorf("container is not running")
   101  	}
   102  	mock.started = false
   103  	mock.factory.notify(Stopped, mock.name)
   104  	return nil
   105  }
   106  
   107  func (mock *MockContainer) IsRunning() bool {
   108  	return mock.started
   109  }
   110  
   111  // String returns information about the container.
   112  func (mock *MockContainer) String() string {
   113  	return fmt.Sprintf("<MockContainer %q>", mock.name)
   114  }
   115  
   116  func (mock *mockFactory) String() string {
   117  	return fmt.Sprintf("<Mock KVM Factory>")
   118  }
   119  
   120  func (mock *mockFactory) New(name string) kvm.Container {
   121  	mock.mu.Lock()
   122  	defer mock.mu.Unlock()
   123  
   124  	container, ok := mock.instances[name]
   125  	if ok {
   126  		return container
   127  	}
   128  	container = &MockContainer{
   129  		factory: mock,
   130  		name:    name,
   131  	}
   132  	mock.instances[name] = container
   133  	return container
   134  }
   135  
   136  func (mock *mockFactory) List() (result []kvm.Container, err error) {
   137  	for _, container := range mock.instances {
   138  		result = append(result, container)
   139  	}
   140  	return
   141  }
   142  
   143  func (mock *mockFactory) notify(action Action, instanceId string) {
   144  	event := Event{action, instanceId}
   145  	for _, c := range mock.listeners {
   146  		c <- event
   147  	}
   148  }
   149  
   150  func (mock *mockFactory) AddListener(listener chan<- Event) {
   151  	mock.listeners = append(mock.listeners, listener)
   152  }
   153  
   154  func (mock *mockFactory) RemoveListener(listener chan<- Event) {
   155  	pos := 0
   156  	for i, c := range mock.listeners {
   157  		if c == listener {
   158  			pos = i
   159  		}
   160  	}
   161  	mock.listeners = append(mock.listeners[:pos], mock.listeners[pos+1:]...)
   162  }
   163  
   164  func (mock *mockFactory) HasListener(listener chan<- Event) bool {
   165  	for _, c := range mock.listeners {
   166  		if c == listener {
   167  			return true
   168  		}
   169  	}
   170  	return false
   171  }