github.com/netdata/go.d.plugin@v0.58.1/modules/docker_engine/docker_engine_test.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package docker_engine 4 5 import ( 6 "net/http" 7 "net/http/httptest" 8 "os" 9 "testing" 10 11 "github.com/netdata/go.d.plugin/pkg/tlscfg" 12 "github.com/netdata/go.d.plugin/pkg/web" 13 14 "github.com/netdata/go.d.plugin/agent/module" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 ) 18 19 var ( 20 metricsNonDockerEngine, _ = os.ReadFile("testdata/non-docker-engine.txt") 21 metricsV17050CE, _ = os.ReadFile("testdata/v17.05.0-ce.txt") 22 metricsV18093CE, _ = os.ReadFile("testdata/v18.09.3-ce.txt") 23 metricsV18093CESwarm, _ = os.ReadFile("testdata/v18.09.3-ce-swarm.txt") 24 ) 25 26 func Test_readTestData(t *testing.T) { 27 assert.NotNil(t, metricsNonDockerEngine) 28 assert.NotNil(t, metricsV17050CE) 29 assert.NotNil(t, metricsV18093CE) 30 assert.NotNil(t, metricsV18093CESwarm) 31 } 32 33 func TestNew(t *testing.T) { 34 assert.Implements(t, (*module.Module)(nil), New()) 35 } 36 37 func TestDockerEngine_Cleanup(t *testing.T) { 38 assert.NotPanics(t, New().Cleanup) 39 } 40 41 func TestDockerEngine_Init(t *testing.T) { 42 tests := map[string]struct { 43 config Config 44 wantFail bool 45 }{ 46 "default": { 47 config: New().Config, 48 }, 49 "empty URL": { 50 config: Config{HTTP: web.HTTP{Request: web.Request{URL: ""}}}, 51 wantFail: true, 52 }, 53 "nonexistent TLS CA": { 54 config: Config{HTTP: web.HTTP{ 55 Request: web.Request{URL: "http://127.0.0.1:9323/metrics"}, 56 Client: web.Client{TLSConfig: tlscfg.TLSConfig{TLSCA: "testdata/tls"}}}}, 57 wantFail: true, 58 }, 59 } 60 61 for name, test := range tests { 62 t.Run(name, func(t *testing.T) { 63 dockerEngine := New() 64 dockerEngine.Config = test.config 65 66 if test.wantFail { 67 assert.False(t, dockerEngine.Init()) 68 } else { 69 assert.True(t, dockerEngine.Init()) 70 } 71 }) 72 } 73 } 74 75 func TestDockerEngine_Check(t *testing.T) { 76 tests := map[string]struct { 77 prepare func(*testing.T) (*DockerEngine, *httptest.Server) 78 wantFail bool 79 }{ 80 "v17.05.0-ce": {prepare: prepareClientServerV17050CE}, 81 "v18.09.3-ce": {prepare: prepareClientServerV18093CE}, 82 "v18.09.3-ce-swarm": {prepare: prepareClientServerV18093CESwarm}, 83 "non docker engine": {prepare: prepareClientServerNonDockerEngine, wantFail: true}, 84 "invalid data": {prepare: prepareClientServerInvalidData, wantFail: true}, 85 "404": {prepare: prepareClientServer404, wantFail: true}, 86 "connection refused": {prepare: prepareClientServerConnectionRefused, wantFail: true}, 87 } 88 89 for name, test := range tests { 90 t.Run(name, func(t *testing.T) { 91 dockerEngine, srv := test.prepare(t) 92 defer srv.Close() 93 94 if test.wantFail { 95 assert.False(t, dockerEngine.Check()) 96 } else { 97 assert.True(t, dockerEngine.Check()) 98 } 99 }) 100 } 101 } 102 103 func TestDockerEngine_Charts(t *testing.T) { 104 tests := map[string]struct { 105 prepare func(*testing.T) (*DockerEngine, *httptest.Server) 106 wantNumCharts int 107 }{ 108 "v17.05.0-ce": {prepare: prepareClientServerV17050CE, wantNumCharts: len(charts) - 1}, // no container states chart 109 "v18.09.3-ce": {prepare: prepareClientServerV18093CE, wantNumCharts: len(charts)}, 110 "v18.09.3-ce-swarm": {prepare: prepareClientServerV18093CESwarm, wantNumCharts: len(charts) + len(swarmManagerCharts)}, 111 } 112 113 for name, test := range tests { 114 t.Run(name, func(t *testing.T) { 115 dockerEngine, srv := test.prepare(t) 116 defer srv.Close() 117 118 require.True(t, dockerEngine.Check()) 119 assert.Len(t, *dockerEngine.Charts(), test.wantNumCharts) 120 }) 121 } 122 } 123 124 func TestDockerEngine_Collect_ReturnsNilOnErrors(t *testing.T) { 125 tests := map[string]struct { 126 prepare func(*testing.T) (*DockerEngine, *httptest.Server) 127 }{ 128 "non docker engine": {prepare: prepareClientServerNonDockerEngine}, 129 "invalid data": {prepare: prepareClientServerInvalidData}, 130 "404": {prepare: prepareClientServer404}, 131 "connection refused": {prepare: prepareClientServerConnectionRefused}, 132 } 133 134 for name, test := range tests { 135 t.Run(name, func(t *testing.T) { 136 dockerEngine, srv := test.prepare(t) 137 defer srv.Close() 138 139 assert.Nil(t, dockerEngine.Collect()) 140 }) 141 } 142 } 143 144 func TestDockerEngine_Collect(t *testing.T) { 145 tests := map[string]struct { 146 prepare func(*testing.T) (*DockerEngine, *httptest.Server) 147 expected map[string]int64 148 }{ 149 "v17.05.0-ce": { 150 prepare: prepareClientServerV17050CE, 151 expected: map[string]int64{ 152 "builder_fails_build_canceled": 1, 153 "builder_fails_build_target_not_reachable_error": 2, 154 "builder_fails_command_not_supported_error": 3, 155 "builder_fails_dockerfile_empty_error": 4, 156 "builder_fails_dockerfile_syntax_error": 5, 157 "builder_fails_error_processing_commands_error": 6, 158 "builder_fails_missing_onbuild_arguments_error": 7, 159 "builder_fails_unknown_instruction_error": 8, 160 "container_actions_changes": 1, 161 "container_actions_commit": 1, 162 "container_actions_create": 1, 163 "container_actions_delete": 1, 164 "container_actions_start": 1, 165 "health_checks_failed": 33, 166 }, 167 }, 168 "v18.09.3-ce": { 169 prepare: prepareClientServerV18093CE, 170 expected: map[string]int64{ 171 "builder_fails_build_canceled": 1, 172 "builder_fails_build_target_not_reachable_error": 2, 173 "builder_fails_command_not_supported_error": 3, 174 "builder_fails_dockerfile_empty_error": 4, 175 "builder_fails_dockerfile_syntax_error": 5, 176 "builder_fails_error_processing_commands_error": 6, 177 "builder_fails_missing_onbuild_arguments_error": 7, 178 "builder_fails_unknown_instruction_error": 8, 179 "container_actions_changes": 1, 180 "container_actions_commit": 1, 181 "container_actions_create": 1, 182 "container_actions_delete": 1, 183 "container_actions_start": 1, 184 "container_states_paused": 11, 185 "container_states_running": 12, 186 "container_states_stopped": 13, 187 "health_checks_failed": 33, 188 }, 189 }, 190 "v18.09.3-ce-swarm": { 191 prepare: prepareClientServerV18093CESwarm, 192 expected: map[string]int64{ 193 "builder_fails_build_canceled": 1, 194 "builder_fails_build_target_not_reachable_error": 2, 195 "builder_fails_command_not_supported_error": 3, 196 "builder_fails_dockerfile_empty_error": 4, 197 "builder_fails_dockerfile_syntax_error": 5, 198 "builder_fails_error_processing_commands_error": 6, 199 "builder_fails_missing_onbuild_arguments_error": 7, 200 "builder_fails_unknown_instruction_error": 8, 201 "container_actions_changes": 1, 202 "container_actions_commit": 1, 203 "container_actions_create": 1, 204 "container_actions_delete": 1, 205 "container_actions_start": 1, 206 "container_states_paused": 11, 207 "container_states_running": 12, 208 "container_states_stopped": 13, 209 "health_checks_failed": 33, 210 "swarm_manager_configs_total": 1, 211 "swarm_manager_leader": 1, 212 "swarm_manager_networks_total": 3, 213 "swarm_manager_nodes_state_disconnected": 1, 214 "swarm_manager_nodes_state_down": 2, 215 "swarm_manager_nodes_state_ready": 3, 216 "swarm_manager_nodes_state_unknown": 4, 217 "swarm_manager_nodes_total": 10, 218 "swarm_manager_secrets_total": 1, 219 "swarm_manager_services_total": 1, 220 "swarm_manager_tasks_state_accepted": 1, 221 "swarm_manager_tasks_state_assigned": 2, 222 "swarm_manager_tasks_state_complete": 3, 223 "swarm_manager_tasks_state_failed": 4, 224 "swarm_manager_tasks_state_new": 5, 225 "swarm_manager_tasks_state_orphaned": 6, 226 "swarm_manager_tasks_state_pending": 7, 227 "swarm_manager_tasks_state_preparing": 8, 228 "swarm_manager_tasks_state_ready": 9, 229 "swarm_manager_tasks_state_rejected": 10, 230 "swarm_manager_tasks_state_remove": 11, 231 "swarm_manager_tasks_state_running": 12, 232 "swarm_manager_tasks_state_shutdown": 13, 233 "swarm_manager_tasks_state_starting": 14, 234 "swarm_manager_tasks_total": 105, 235 }, 236 }, 237 } 238 239 for name, test := range tests { 240 t.Run(name, func(t *testing.T) { 241 pulsar, srv := test.prepare(t) 242 defer srv.Close() 243 244 for i := 0; i < 10; i++ { 245 _ = pulsar.Collect() 246 } 247 collected := pulsar.Collect() 248 249 require.NotNil(t, collected) 250 require.Equal(t, test.expected, collected) 251 ensureCollectedHasAllChartsDimsVarsIDs(t, pulsar, collected) 252 }) 253 } 254 } 255 256 func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, dockerEngine *DockerEngine, collected map[string]int64) { 257 t.Helper() 258 for _, chart := range *dockerEngine.Charts() { 259 for _, dim := range chart.Dims { 260 _, ok := collected[dim.ID] 261 assert.Truef(t, ok, "collected metrics has no data for dim '%s' chart '%s'", dim.ID, chart.ID) 262 } 263 for _, v := range chart.Vars { 264 _, ok := collected[v.ID] 265 assert.Truef(t, ok, "collected metrics has no data for var '%s' chart '%s'", v.ID, chart.ID) 266 } 267 } 268 } 269 270 func prepareClientServerV17050CE(t *testing.T) (*DockerEngine, *httptest.Server) { 271 t.Helper() 272 srv := httptest.NewServer(http.HandlerFunc( 273 func(w http.ResponseWriter, r *http.Request) { 274 _, _ = w.Write(metricsV17050CE) 275 })) 276 277 dockerEngine := New() 278 dockerEngine.URL = srv.URL 279 require.True(t, dockerEngine.Init()) 280 281 return dockerEngine, srv 282 } 283 284 func prepareClientServerV18093CE(t *testing.T) (*DockerEngine, *httptest.Server) { 285 t.Helper() 286 srv := httptest.NewServer(http.HandlerFunc( 287 func(w http.ResponseWriter, r *http.Request) { 288 _, _ = w.Write(metricsV18093CE) 289 })) 290 291 dockerEngine := New() 292 dockerEngine.URL = srv.URL 293 require.True(t, dockerEngine.Init()) 294 295 return dockerEngine, srv 296 } 297 298 func prepareClientServerV18093CESwarm(t *testing.T) (*DockerEngine, *httptest.Server) { 299 t.Helper() 300 srv := httptest.NewServer(http.HandlerFunc( 301 func(w http.ResponseWriter, r *http.Request) { 302 _, _ = w.Write(metricsV18093CESwarm) 303 })) 304 305 dockerEngine := New() 306 dockerEngine.URL = srv.URL 307 require.True(t, dockerEngine.Init()) 308 309 return dockerEngine, srv 310 } 311 312 func prepareClientServerNonDockerEngine(t *testing.T) (*DockerEngine, *httptest.Server) { 313 t.Helper() 314 srv := httptest.NewServer(http.HandlerFunc( 315 func(w http.ResponseWriter, r *http.Request) { 316 _, _ = w.Write(metricsNonDockerEngine) 317 })) 318 319 dockerEngine := New() 320 dockerEngine.URL = srv.URL 321 require.True(t, dockerEngine.Init()) 322 323 return dockerEngine, srv 324 } 325 326 func prepareClientServerInvalidData(t *testing.T) (*DockerEngine, *httptest.Server) { 327 t.Helper() 328 srv := httptest.NewServer(http.HandlerFunc( 329 func(w http.ResponseWriter, r *http.Request) { 330 _, _ = w.Write([]byte("hello and\n goodbye")) 331 })) 332 333 dockerEngine := New() 334 dockerEngine.URL = srv.URL 335 require.True(t, dockerEngine.Init()) 336 337 return dockerEngine, srv 338 } 339 340 func prepareClientServer404(t *testing.T) (*DockerEngine, *httptest.Server) { 341 t.Helper() 342 srv := httptest.NewServer(http.HandlerFunc( 343 func(w http.ResponseWriter, r *http.Request) { 344 w.WriteHeader(http.StatusNotFound) 345 })) 346 347 dockerEngine := New() 348 dockerEngine.URL = srv.URL 349 require.True(t, dockerEngine.Init()) 350 351 return dockerEngine, srv 352 } 353 354 func prepareClientServerConnectionRefused(t *testing.T) (*DockerEngine, *httptest.Server) { 355 t.Helper() 356 srv := httptest.NewServer(nil) 357 358 dockerEngine := New() 359 dockerEngine.URL = "http://127.0.0.1:38001/metrics" 360 require.True(t, dockerEngine.Init()) 361 362 return dockerEngine, srv 363 }