github.com/uchennaokeke444/nomad@v0.11.8/nomad/deploymentwatcher/testutil_test.go (about) 1 package deploymentwatcher 2 3 import ( 4 "reflect" 5 "strings" 6 "sync" 7 "testing" 8 9 "github.com/hashicorp/nomad/nomad/state" 10 "github.com/hashicorp/nomad/nomad/structs" 11 mocker "github.com/stretchr/testify/mock" 12 ) 13 14 type mockBackend struct { 15 mocker.Mock 16 index uint64 17 state *state.StateStore 18 l sync.Mutex 19 } 20 21 func newMockBackend(t *testing.T) *mockBackend { 22 m := &mockBackend{ 23 index: 10000, 24 state: state.TestStateStore(t), 25 } 26 m.Test(t) 27 return m 28 } 29 30 func (m *mockBackend) nextIndex() uint64 { 31 m.l.Lock() 32 defer m.l.Unlock() 33 i := m.index 34 m.index++ 35 return i 36 } 37 38 func (m *mockBackend) UpdateAllocDesiredTransition(u *structs.AllocUpdateDesiredTransitionRequest) (uint64, error) { 39 m.Called(u) 40 i := m.nextIndex() 41 return i, m.state.UpdateAllocsDesiredTransitions(i, u.Allocs, u.Evals) 42 } 43 44 // matchUpdateAllocDesiredTransitions is used to match an upsert request 45 func matchUpdateAllocDesiredTransitions(deploymentIDs []string) func(update *structs.AllocUpdateDesiredTransitionRequest) bool { 46 return func(update *structs.AllocUpdateDesiredTransitionRequest) bool { 47 if len(update.Evals) != len(deploymentIDs) { 48 return false 49 } 50 51 dmap := make(map[string]struct{}, len(deploymentIDs)) 52 for _, d := range deploymentIDs { 53 dmap[d] = struct{}{} 54 } 55 56 for _, e := range update.Evals { 57 if _, ok := dmap[e.DeploymentID]; !ok { 58 return false 59 } 60 61 delete(dmap, e.DeploymentID) 62 } 63 64 return true 65 } 66 } 67 68 // matchUpdateAllocDesiredTransitionReschedule is used to match allocs that have their DesiredTransition set to Reschedule 69 func matchUpdateAllocDesiredTransitionReschedule(allocIDs []string) func(update *structs.AllocUpdateDesiredTransitionRequest) bool { 70 return func(update *structs.AllocUpdateDesiredTransitionRequest) bool { 71 amap := make(map[string]struct{}, len(allocIDs)) 72 for _, d := range allocIDs { 73 amap[d] = struct{}{} 74 } 75 76 for allocID, dt := range update.Allocs { 77 if _, ok := amap[allocID]; !ok { 78 return false 79 } 80 if !*dt.Reschedule { 81 return false 82 } 83 } 84 85 return true 86 } 87 } 88 89 func (m *mockBackend) UpsertJob(job *structs.Job) (uint64, error) { 90 m.Called(job) 91 i := m.nextIndex() 92 return i, m.state.UpsertJob(i, job) 93 } 94 95 func (m *mockBackend) UpdateDeploymentStatus(u *structs.DeploymentStatusUpdateRequest) (uint64, error) { 96 m.Called(u) 97 i := m.nextIndex() 98 return i, m.state.UpdateDeploymentStatus(i, u) 99 } 100 101 // matchDeploymentStatusUpdateConfig is used to configure the matching 102 // function 103 type matchDeploymentStatusUpdateConfig struct { 104 // DeploymentID is the expected ID 105 DeploymentID string 106 107 // Status is the desired status 108 Status string 109 110 // StatusDescription is the desired status description 111 StatusDescription string 112 113 // JobVersion marks whether we expect a roll back job at the given version 114 JobVersion *uint64 115 116 // Eval marks whether we expect an evaluation. 117 Eval bool 118 } 119 120 // matchDeploymentStatusUpdateRequest is used to match an update request 121 func matchDeploymentStatusUpdateRequest(c *matchDeploymentStatusUpdateConfig) func(args *structs.DeploymentStatusUpdateRequest) bool { 122 return func(args *structs.DeploymentStatusUpdateRequest) bool { 123 if args.DeploymentUpdate.DeploymentID != c.DeploymentID { 124 return false 125 } 126 127 if args.DeploymentUpdate.Status != c.Status && args.DeploymentUpdate.StatusDescription != c.StatusDescription { 128 return false 129 } 130 131 if c.Eval && args.Eval == nil || !c.Eval && args.Eval != nil { 132 return false 133 } 134 135 if c.JobVersion != nil { 136 if args.Job == nil { 137 return false 138 } else if args.Job.Version != *c.JobVersion { 139 return false 140 } 141 } else if c.JobVersion == nil && args.Job != nil { 142 return false 143 } 144 145 return true 146 } 147 } 148 149 func (m *mockBackend) UpdateDeploymentPromotion(req *structs.ApplyDeploymentPromoteRequest) (uint64, error) { 150 m.Called(req) 151 i := m.nextIndex() 152 return i, m.state.UpdateDeploymentPromotion(i, req) 153 } 154 155 // matchDeploymentPromoteRequestConfig is used to configure the matching 156 // function 157 type matchDeploymentPromoteRequestConfig struct { 158 // Promotion holds the expected promote request 159 Promotion *structs.DeploymentPromoteRequest 160 161 // Eval marks whether we expect an evaluation. 162 Eval bool 163 } 164 165 // matchDeploymentPromoteRequest is used to match a promote request 166 func matchDeploymentPromoteRequest(c *matchDeploymentPromoteRequestConfig) func(args *structs.ApplyDeploymentPromoteRequest) bool { 167 return func(args *structs.ApplyDeploymentPromoteRequest) bool { 168 if !reflect.DeepEqual(*c.Promotion, args.DeploymentPromoteRequest) { 169 return false 170 } 171 172 if c.Eval && args.Eval == nil || !c.Eval && args.Eval != nil { 173 return false 174 } 175 176 return true 177 } 178 } 179 func (m *mockBackend) UpdateDeploymentAllocHealth(req *structs.ApplyDeploymentAllocHealthRequest) (uint64, error) { 180 m.Called(req) 181 i := m.nextIndex() 182 return i, m.state.UpdateDeploymentAllocHealth(i, req) 183 } 184 185 // matchDeploymentAllocHealthRequestConfig is used to configure the matching 186 // function 187 type matchDeploymentAllocHealthRequestConfig struct { 188 // DeploymentID is the expected ID 189 DeploymentID string 190 191 // Healthy and Unhealthy contain the expected allocation IDs that are having 192 // their health set 193 Healthy, Unhealthy []string 194 195 // DeploymentUpdate holds the expected values of status and description. We 196 // don't check for exact match but string contains 197 DeploymentUpdate *structs.DeploymentStatusUpdate 198 199 // JobVersion marks whether we expect a roll back job at the given version 200 JobVersion *uint64 201 202 // Eval marks whether we expect an evaluation. 203 Eval bool 204 } 205 206 // matchDeploymentAllocHealthRequest is used to match an update request 207 func matchDeploymentAllocHealthRequest(c *matchDeploymentAllocHealthRequestConfig) func(args *structs.ApplyDeploymentAllocHealthRequest) bool { 208 return func(args *structs.ApplyDeploymentAllocHealthRequest) bool { 209 if args.DeploymentID != c.DeploymentID { 210 return false 211 } 212 213 // Require a timestamp 214 if args.Timestamp.IsZero() { 215 return false 216 } 217 218 if len(c.Healthy) != len(args.HealthyAllocationIDs) { 219 return false 220 } 221 if len(c.Unhealthy) != len(args.UnhealthyAllocationIDs) { 222 return false 223 } 224 225 hmap, umap := make(map[string]struct{}, len(c.Healthy)), make(map[string]struct{}, len(c.Unhealthy)) 226 for _, h := range c.Healthy { 227 hmap[h] = struct{}{} 228 } 229 for _, u := range c.Unhealthy { 230 umap[u] = struct{}{} 231 } 232 233 for _, h := range args.HealthyAllocationIDs { 234 if _, ok := hmap[h]; !ok { 235 return false 236 } 237 } 238 for _, u := range args.UnhealthyAllocationIDs { 239 if _, ok := umap[u]; !ok { 240 return false 241 } 242 } 243 244 if c.DeploymentUpdate != nil { 245 if args.DeploymentUpdate == nil { 246 return false 247 } 248 249 if !strings.Contains(args.DeploymentUpdate.Status, c.DeploymentUpdate.Status) { 250 return false 251 } 252 if !strings.Contains(args.DeploymentUpdate.StatusDescription, c.DeploymentUpdate.StatusDescription) { 253 return false 254 } 255 } else if args.DeploymentUpdate != nil { 256 return false 257 } 258 259 if c.Eval && args.Eval == nil || !c.Eval && args.Eval != nil { 260 return false 261 } 262 263 if (c.JobVersion != nil && (args.Job == nil || args.Job.Version != *c.JobVersion)) || c.JobVersion == nil && args.Job != nil { 264 return false 265 } 266 267 return true 268 } 269 }