1/* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15*/ 16 17package server 18 19import ( 20 "testing" 21 "time" 22 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 "golang.org/x/net/context" 26 runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2" 27 28 containerstore "github.com/containerd/containerd/pkg/cri/store/container" 29 sandboxstore "github.com/containerd/containerd/pkg/cri/store/sandbox" 30) 31 32func TestToCRIContainer(t *testing.T) { 33 config := &runtime.ContainerConfig{ 34 Metadata: &runtime.ContainerMetadata{ 35 Name: "test-name", 36 Attempt: 1, 37 }, 38 Image: &runtime.ImageSpec{Image: "test-image"}, 39 Labels: map[string]string{"a": "b"}, 40 Annotations: map[string]string{"c": "d"}, 41 } 42 createdAt := time.Now().UnixNano() 43 container, err := containerstore.NewContainer( 44 containerstore.Metadata{ 45 ID: "test-id", 46 Name: "test-name", 47 SandboxID: "test-sandbox-id", 48 Config: config, 49 ImageRef: "test-image-ref", 50 }, 51 containerstore.WithFakeStatus( 52 containerstore.Status{ 53 Pid: 1234, 54 CreatedAt: createdAt, 55 StartedAt: time.Now().UnixNano(), 56 FinishedAt: time.Now().UnixNano(), 57 ExitCode: 1, 58 Reason: "test-reason", 59 Message: "test-message", 60 }, 61 ), 62 ) 63 assert.NoError(t, err) 64 expect := &runtime.Container{ 65 Id: "test-id", 66 PodSandboxId: "test-sandbox-id", 67 Metadata: config.GetMetadata(), 68 Image: config.GetImage(), 69 ImageRef: "test-image-ref", 70 State: runtime.ContainerState_CONTAINER_EXITED, 71 CreatedAt: createdAt, 72 Labels: config.GetLabels(), 73 Annotations: config.GetAnnotations(), 74 } 75 c := toCRIContainer(container) 76 assert.Equal(t, expect, c) 77} 78 79func TestFilterContainers(t *testing.T) { 80 c := newTestCRIService() 81 82 testContainers := []*runtime.Container{ 83 { 84 Id: "1", 85 PodSandboxId: "s-1", 86 Metadata: &runtime.ContainerMetadata{Name: "name-1", Attempt: 1}, 87 State: runtime.ContainerState_CONTAINER_RUNNING, 88 }, 89 { 90 Id: "2", 91 PodSandboxId: "s-2", 92 Metadata: &runtime.ContainerMetadata{Name: "name-2", Attempt: 2}, 93 State: runtime.ContainerState_CONTAINER_EXITED, 94 Labels: map[string]string{"a": "b"}, 95 }, 96 { 97 Id: "3", 98 PodSandboxId: "s-2", 99 Metadata: &runtime.ContainerMetadata{Name: "name-2", Attempt: 3}, 100 State: runtime.ContainerState_CONTAINER_CREATED, 101 Labels: map[string]string{"c": "d"}, 102 }, 103 } 104 for desc, test := range map[string]struct { 105 filter *runtime.ContainerFilter 106 expect []*runtime.Container 107 }{ 108 "no filter": { 109 expect: testContainers, 110 }, 111 "id filter": { 112 filter: &runtime.ContainerFilter{Id: "2"}, 113 expect: []*runtime.Container{testContainers[1]}, 114 }, 115 "state filter": { 116 filter: &runtime.ContainerFilter{ 117 State: &runtime.ContainerStateValue{ 118 State: runtime.ContainerState_CONTAINER_EXITED, 119 }, 120 }, 121 expect: []*runtime.Container{testContainers[1]}, 122 }, 123 "label filter": { 124 filter: &runtime.ContainerFilter{ 125 LabelSelector: map[string]string{"a": "b"}, 126 }, 127 expect: []*runtime.Container{testContainers[1]}, 128 }, 129 "sandbox id filter": { 130 filter: &runtime.ContainerFilter{PodSandboxId: "s-2"}, 131 expect: []*runtime.Container{testContainers[1], testContainers[2]}, 132 }, 133 "mixed filter not matched": { 134 filter: &runtime.ContainerFilter{ 135 Id: "1", 136 PodSandboxId: "s-2", 137 LabelSelector: map[string]string{"a": "b"}, 138 }, 139 expect: []*runtime.Container{}, 140 }, 141 "mixed filter matched": { 142 filter: &runtime.ContainerFilter{ 143 PodSandboxId: "s-2", 144 State: &runtime.ContainerStateValue{ 145 State: runtime.ContainerState_CONTAINER_CREATED, 146 }, 147 LabelSelector: map[string]string{"c": "d"}, 148 }, 149 expect: []*runtime.Container{testContainers[2]}, 150 }, 151 } { 152 filtered := c.filterCRIContainers(testContainers, test.filter) 153 assert.Equal(t, test.expect, filtered, desc) 154 } 155} 156 157// containerForTest is a helper type for test. 158type containerForTest struct { 159 metadata containerstore.Metadata 160 status containerstore.Status 161} 162 163func (c containerForTest) toContainer() (containerstore.Container, error) { 164 return containerstore.NewContainer( 165 c.metadata, 166 containerstore.WithFakeStatus(c.status), 167 ) 168} 169 170func TestListContainers(t *testing.T) { 171 c := newTestCRIService() 172 sandboxesInStore := []sandboxstore.Sandbox{ 173 sandboxstore.NewSandbox( 174 sandboxstore.Metadata{ 175 ID: "s-1abcdef1234", 176 Name: "sandboxname-1", 177 Config: &runtime.PodSandboxConfig{Metadata: &runtime.PodSandboxMetadata{Name: "podname-1"}}, 178 }, 179 sandboxstore.Status{ 180 State: sandboxstore.StateReady, 181 }, 182 ), 183 sandboxstore.NewSandbox( 184 sandboxstore.Metadata{ 185 ID: "s-2abcdef1234", 186 Name: "sandboxname-2", 187 Config: &runtime.PodSandboxConfig{Metadata: &runtime.PodSandboxMetadata{Name: "podname-2"}}, 188 }, 189 sandboxstore.Status{ 190 State: sandboxstore.StateNotReady, 191 }, 192 ), 193 } 194 createdAt := time.Now().UnixNano() 195 startedAt := time.Now().UnixNano() 196 finishedAt := time.Now().UnixNano() 197 containersInStore := []containerForTest{ 198 { 199 metadata: containerstore.Metadata{ 200 ID: "c-1container", 201 Name: "name-1", 202 SandboxID: "s-1abcdef1234", 203 Config: &runtime.ContainerConfig{Metadata: &runtime.ContainerMetadata{Name: "name-1"}}, 204 }, 205 status: containerstore.Status{CreatedAt: createdAt}, 206 }, 207 { 208 metadata: containerstore.Metadata{ 209 ID: "c-2container", 210 Name: "name-2", 211 SandboxID: "s-1abcdef1234", 212 Config: &runtime.ContainerConfig{Metadata: &runtime.ContainerMetadata{Name: "name-2"}}, 213 }, 214 status: containerstore.Status{ 215 CreatedAt: createdAt, 216 StartedAt: startedAt, 217 }, 218 }, 219 { 220 metadata: containerstore.Metadata{ 221 ID: "c-3container", 222 Name: "name-3", 223 SandboxID: "s-1abcdef1234", 224 Config: &runtime.ContainerConfig{Metadata: &runtime.ContainerMetadata{Name: "name-3"}}, 225 }, 226 status: containerstore.Status{ 227 CreatedAt: createdAt, 228 StartedAt: startedAt, 229 FinishedAt: finishedAt, 230 }, 231 }, 232 { 233 metadata: containerstore.Metadata{ 234 ID: "c-4container", 235 Name: "name-4", 236 SandboxID: "s-2abcdef1234", 237 Config: &runtime.ContainerConfig{Metadata: &runtime.ContainerMetadata{Name: "name-4"}}, 238 }, 239 status: containerstore.Status{ 240 CreatedAt: createdAt, 241 }, 242 }, 243 } 244 245 expectedContainers := []*runtime.Container{ 246 { 247 Id: "c-1container", 248 PodSandboxId: "s-1abcdef1234", 249 Metadata: &runtime.ContainerMetadata{Name: "name-1"}, 250 State: runtime.ContainerState_CONTAINER_CREATED, 251 CreatedAt: createdAt, 252 }, 253 { 254 Id: "c-2container", 255 PodSandboxId: "s-1abcdef1234", 256 Metadata: &runtime.ContainerMetadata{Name: "name-2"}, 257 State: runtime.ContainerState_CONTAINER_RUNNING, 258 CreatedAt: createdAt, 259 }, 260 { 261 Id: "c-3container", 262 PodSandboxId: "s-1abcdef1234", 263 Metadata: &runtime.ContainerMetadata{Name: "name-3"}, 264 State: runtime.ContainerState_CONTAINER_EXITED, 265 CreatedAt: createdAt, 266 }, 267 { 268 Id: "c-4container", 269 PodSandboxId: "s-2abcdef1234", 270 Metadata: &runtime.ContainerMetadata{Name: "name-4"}, 271 State: runtime.ContainerState_CONTAINER_CREATED, 272 CreatedAt: createdAt, 273 }, 274 } 275 276 // Inject test sandbox metadata 277 for _, sb := range sandboxesInStore { 278 assert.NoError(t, c.sandboxStore.Add(sb)) 279 } 280 281 // Inject test container metadata 282 for _, cntr := range containersInStore { 283 container, err := cntr.toContainer() 284 assert.NoError(t, err) 285 assert.NoError(t, c.containerStore.Add(container)) 286 } 287 288 for testdesc, testdata := range map[string]struct { 289 filter *runtime.ContainerFilter 290 expect []*runtime.Container 291 }{ 292 "test without filter": { 293 filter: &runtime.ContainerFilter{}, 294 expect: expectedContainers, 295 }, 296 "test filter by sandboxid": { 297 filter: &runtime.ContainerFilter{ 298 PodSandboxId: "s-1abcdef1234", 299 }, 300 expect: expectedContainers[:3], 301 }, 302 "test filter by truncated sandboxid": { 303 filter: &runtime.ContainerFilter{ 304 PodSandboxId: "s-1", 305 }, 306 expect: expectedContainers[:3], 307 }, 308 "test filter by containerid": { 309 filter: &runtime.ContainerFilter{ 310 Id: "c-1container", 311 }, 312 expect: expectedContainers[:1], 313 }, 314 "test filter by truncated containerid": { 315 filter: &runtime.ContainerFilter{ 316 Id: "c-1", 317 }, 318 expect: expectedContainers[:1], 319 }, 320 "test filter by containerid and sandboxid": { 321 filter: &runtime.ContainerFilter{ 322 Id: "c-1container", 323 PodSandboxId: "s-1abcdef1234", 324 }, 325 expect: expectedContainers[:1], 326 }, 327 "test filter by truncated containerid and truncated sandboxid": { 328 filter: &runtime.ContainerFilter{ 329 Id: "c-1", 330 PodSandboxId: "s-1", 331 }, 332 expect: expectedContainers[:1], 333 }, 334 } { 335 t.Logf("TestCase: %s", testdesc) 336 resp, err := c.ListContainers(context.Background(), &runtime.ListContainersRequest{Filter: testdata.filter}) 337 assert.NoError(t, err) 338 require.NotNil(t, resp) 339 containers := resp.GetContainers() 340 assert.Len(t, containers, len(testdata.expect)) 341 for _, cntr := range testdata.expect { 342 assert.Contains(t, containers, cntr) 343 } 344 } 345} 346