github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/caasbroker/fixture_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package caasbroker_test 5 6 import ( 7 "context" 8 "sync" 9 10 "github.com/juju/names/v5" 11 "github.com/juju/testing" 12 "github.com/juju/worker/v3" 13 "github.com/juju/worker/v3/workertest" 14 gc "gopkg.in/check.v1" 15 16 "github.com/juju/juju/caas" 17 "github.com/juju/juju/controller" 18 "github.com/juju/juju/core/watcher" 19 "github.com/juju/juju/environs" 20 environscloudspec "github.com/juju/juju/environs/cloudspec" 21 "github.com/juju/juju/environs/config" 22 jujutesting "github.com/juju/juju/testing" 23 ) 24 25 type fixture struct { 26 watcherErr error 27 observerErrs []error 28 initialConfig map[string]interface{} 29 initialSpec environscloudspec.CloudSpec 30 } 31 32 func (fix *fixture) Run(c *gc.C, test func(*runContext)) { 33 watcher := newNotifyWatcher(fix.watcherErr) 34 defer workertest.DirtyKill(c, watcher) 35 cloudWatcher := newNotifyWatcher(fix.watcherErr) 36 defer workertest.DirtyKill(c, cloudWatcher) 37 context := &runContext{ 38 cloud: fix.initialSpec, 39 config: newModelConfig(c, fix.initialConfig), 40 watcher: watcher, 41 cloudWatcher: cloudWatcher, 42 } 43 context.stub.SetErrors(fix.observerErrs...) 44 test(context) 45 } 46 47 type runContext struct { 48 mu sync.Mutex 49 stub testing.Stub 50 cloud environscloudspec.CloudSpec 51 config map[string]interface{} 52 watcher *notifyWatcher 53 cloudWatcher *notifyWatcher 54 credWatcher *notifyWatcher 55 } 56 57 // SetConfig updates the configuration returned by ModelConfig. 58 func (context *runContext) SetConfig(c *gc.C, extraAttrs jujutesting.Attrs) { 59 context.mu.Lock() 60 defer context.mu.Unlock() 61 context.config = newModelConfig(c, extraAttrs) 62 } 63 64 // SetCloudSpec updates the spec returned by CloudSpec. 65 func (context *runContext) SetCloudSpec(c *gc.C, spec environscloudspec.CloudSpec) { 66 context.mu.Lock() 67 defer context.mu.Unlock() 68 context.cloud = spec 69 } 70 71 // CloudSpec is part of the environ.ConfigObserver interface. 72 func (context *runContext) CloudSpec() (environscloudspec.CloudSpec, error) { 73 context.mu.Lock() 74 defer context.mu.Unlock() 75 context.stub.AddCall("CloudSpec") 76 if err := context.stub.NextErr(); err != nil { 77 return environscloudspec.CloudSpec{}, err 78 } 79 return context.cloud, nil 80 } 81 82 // ModelConfig is part of the environ.ConfigObserver interface. 83 func (context *runContext) ModelConfig() (*config.Config, error) { 84 context.mu.Lock() 85 defer context.mu.Unlock() 86 context.stub.AddCall("ModelConfig") 87 if err := context.stub.NextErr(); err != nil { 88 return nil, err 89 } 90 return config.New(config.UseDefaults, context.config) 91 } 92 93 func (context *runContext) ControllerConfig() (controller.Config, error) { 94 context.mu.Lock() 95 defer context.mu.Unlock() 96 context.stub.AddCall("ControllerConfig") 97 if err := context.stub.NextErr(); err != nil { 98 return nil, err 99 } 100 return jujutesting.FakeControllerConfig(), nil 101 } 102 103 // KillModelConfigNotify kills the watcher returned from WatchForModelConfigChanges with 104 // the error configured in the enclosing fixture. 105 func (context *runContext) KillModelConfigNotify() { 106 context.watcher.Kill() 107 } 108 109 // SendModelConfigNotify sends a value on the channel used by WatchForModelConfigChanges 110 // results. 111 func (context *runContext) SendModelConfigNotify() { 112 context.watcher.changes <- struct{}{} 113 } 114 115 // CloseModelConfigNotify closes the channel used by WatchForModelConfigChanges results. 116 func (context *runContext) CloseModelConfigNotify() { 117 close(context.watcher.changes) 118 } 119 120 // KillCloudSpecNotify kills the watcher returned from WatchCloudSpecChanges with 121 // the error configured in the enclosing fixture. 122 func (context *runContext) KillCloudSpecNotify() { 123 context.cloudWatcher.Kill() 124 } 125 126 // SendCloudSpecNotify sends a value on the channel used by WatchCloudSpecChanges 127 // results. 128 func (context *runContext) SendCloudSpecNotify() { 129 context.cloudWatcher.changes <- struct{}{} 130 } 131 132 // CloseCloudSpecNotify closes the channel used by WatchCloudSpecChanges results. 133 func (context *runContext) CloseCloudSpecNotify() { 134 close(context.cloudWatcher.changes) 135 } 136 137 // WatchForModelConfigChanges is part of the environ.ConfigObserver interface. 138 func (context *runContext) WatchForModelConfigChanges() (watcher.NotifyWatcher, error) { 139 context.mu.Lock() 140 defer context.mu.Unlock() 141 context.stub.AddCall("WatchForModelConfigChanges") 142 if err := context.stub.NextErr(); err != nil { 143 return nil, err 144 } 145 return context.watcher, nil 146 } 147 148 func (context *runContext) WatchCloudSpecChanges() (watcher.NotifyWatcher, error) { 149 context.mu.Lock() 150 defer context.mu.Unlock() 151 context.stub.AddCall("WatchCloudSpecChanges") 152 if err := context.stub.NextErr(); err != nil { 153 return nil, err 154 } 155 return context.cloudWatcher, nil 156 } 157 158 // KillCredentialNotify kills the watcher returned from WatchCredentialChanges with 159 // the error configured in the enclosing fixture. 160 func (context *runContext) KillCredentialNotify() { 161 context.credWatcher.Kill() 162 } 163 164 // SendCredentialNotify sends a value on the channel used by WatchCredentialChanges 165 // results. 166 func (context *runContext) SendCredentialNotify() { 167 context.credWatcher.changes <- struct{}{} 168 } 169 170 // CloseCredentialNotify closes the channel used by WatchCredentialChanges results. 171 func (context *runContext) CloseCredentialNotify() { 172 close(context.credWatcher.changes) 173 } 174 175 // WatchCredential is part of the environ.ConfigObserver interface. 176 func (context *runContext) WatchCredential(cred names.CloudCredentialTag) (watcher.NotifyWatcher, error) { 177 context.mu.Lock() 178 defer context.mu.Unlock() 179 context.stub.AddCall("WatchCredential") 180 if err := context.stub.NextErr(); err != nil { 181 return nil, err 182 } 183 return context.watcher, nil 184 } 185 186 func (context *runContext) CheckCallNames(c *gc.C, names ...string) { 187 context.mu.Lock() 188 defer context.mu.Unlock() 189 context.stub.CheckCallNames(c, names...) 190 } 191 192 // newNotifyWatcher returns a watcher.NotifyWatcher that will fail with the 193 // supplied error when Kill()ed. 194 func newNotifyWatcher(err error) *notifyWatcher { 195 return ¬ifyWatcher{ 196 Worker: workertest.NewErrorWorker(err), 197 changes: make(chan struct{}, 1000), 198 } 199 } 200 201 type notifyWatcher struct { 202 worker.Worker 203 changes chan struct{} 204 } 205 206 // Changes is part of the watcher.NotifyWatcher interface. 207 func (w *notifyWatcher) Changes() watcher.NotifyChannel { 208 return w.changes 209 } 210 211 // newModelConfig returns an environment config map with the supplied attrs 212 // (on top of some default set), or fails the test. 213 func newModelConfig(c *gc.C, extraAttrs jujutesting.Attrs) map[string]interface{} { 214 return jujutesting.CustomModelConfig(c, extraAttrs).AllAttrs() 215 } 216 217 type mockBroker struct { 218 caas.Broker 219 testing.Stub 220 spec environscloudspec.CloudSpec 221 cfg *config.Config 222 namespace string 223 mu sync.Mutex 224 } 225 226 func newMockBroker(_ context.Context, args environs.OpenParams) (caas.Broker, error) { 227 return &mockBroker{spec: args.Cloud, namespace: args.Config.Name(), cfg: args.Config}, nil 228 } 229 230 func (e *mockBroker) CloudSpec() environscloudspec.CloudSpec { 231 e.mu.Lock() 232 defer e.mu.Unlock() 233 return e.spec 234 } 235 236 func (e *mockBroker) SetCloudSpec(_ context.Context, spec environscloudspec.CloudSpec) error { 237 e.mu.Lock() 238 defer e.mu.Unlock() 239 e.MethodCall(e, "SetCloudSpec", spec) 240 if err := e.NextErr(); err != nil { 241 return err 242 } 243 e.spec = spec 244 return nil 245 } 246 247 func (e *mockBroker) Config() *config.Config { 248 e.mu.Lock() 249 defer e.mu.Unlock() 250 e.MethodCall(e, "Config") 251 e.PopNoErr() 252 return e.cfg 253 } 254 255 func (e *mockBroker) SetConfig(cfg *config.Config) error { 256 e.mu.Lock() 257 defer e.mu.Unlock() 258 e.MethodCall(e, "SetConfig", cfg) 259 if err := e.NextErr(); err != nil { 260 return err 261 } 262 e.cfg = cfg 263 return nil 264 }