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