github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/configuration/container/container_kill_test.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package container 21 22 import ( 23 "context" 24 "net" 25 "os" 26 "path/filepath" 27 "reflect" 28 "testing" 29 "time" 30 31 "github.com/docker/docker/api/types" 32 "github.com/golang/mock/gomock" 33 "github.com/stretchr/testify/require" 34 "go.uber.org/zap" 35 "golang.org/x/net/http2" 36 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" 37 38 "github.com/1aal/kubeblocks/pkg/configuration/container/mocks" 39 cfgcore "github.com/1aal/kubeblocks/pkg/configuration/core" 40 ) 41 42 var zapLog, _ = zap.NewDevelopment() 43 44 func TestNewContainerKiller(t *testing.T) { 45 zaplog, _ := zap.NewProduction() 46 47 type args struct { 48 criType CRIType 49 socketPath string 50 } 51 tests := []struct { 52 name string 53 args args 54 want ContainerKiller 55 wantErr bool 56 }{{ 57 name: "test1", 58 args: args{ 59 criType: "xxxx", 60 }, 61 wantErr: true, 62 }, { 63 name: "test2", 64 args: args{ 65 criType: ContainerdType, 66 socketPath: "for_test", 67 }, 68 wantErr: false, 69 want: &containerdContainer{ 70 runtimeEndpoint: "for_test", 71 logger: zaplog.Sugar(), 72 }, 73 }, { 74 name: "test3", 75 args: args{ 76 criType: DockerType, 77 }, 78 wantErr: false, 79 want: &dockerContainer{ 80 logger: zaplog.Sugar(), 81 }, 82 }} 83 84 for _, tt := range tests { 85 t.Run(tt.name, func(t *testing.T) { 86 got, err := NewContainerKiller(tt.args.criType, tt.args.socketPath, zaplog.Sugar()) 87 if (err != nil) != tt.wantErr { 88 t.Errorf("NewContainerKiller() error = %v, wantErr %v", err, tt.wantErr) 89 return 90 } 91 if !reflect.DeepEqual(got, tt.want) { 92 t.Errorf("NewContainerKiller() got = %v, want %v", got, tt.want) 93 } 94 }) 95 } 96 } 97 98 func TestDockerContainerKill(t *testing.T) { 99 mockCtrl := gomock.NewController(t) 100 defer mockCtrl.Finish() 101 cli := mocks.NewMockContainerAPIClient(mockCtrl) 102 103 docker := &dockerContainer{ 104 logger: zapLog.Sugar(), 105 dc: cli, 106 } 107 108 // mock ContainerList failed 109 cli.EXPECT().ContainerList(gomock.Any(), gomock.Any()). 110 Return(nil, cfgcore.MakeError("docker service not ready!")) 111 112 // mock container is not exist 113 cli.EXPECT().ContainerList(gomock.Any(), gomock.Any()). 114 Return([]types.Container{ 115 testContainer("docker", "e5a00fc1653e196287576abccb70bac7411f553d09096a16fc2e0d8a66e03a8e", ""), 116 }, nil) 117 118 // mock container is existed 119 cli.EXPECT().ContainerList(gomock.Any(), gomock.Any()). 120 Return([]types.Container{ 121 testContainer("docker", "e5a00fc1653e196287576abccb70bac7411f553d09096a16fc2e0d8a66e03a8e", ""), 122 testContainer("docker", "76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55", "exited"), 123 }, nil) 124 125 // mock container is running 126 cli.EXPECT().ContainerList(gomock.Any(), gomock.Any()). 127 Return([]types.Container{ 128 testContainer("docker", "76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55", "running"), 129 testContainer("docker", "754d7342de7feb16be79462bc9d72b9c37306ec08374e914ae0a8f8377d0d855", "running"), 130 }, nil).AnyTimes() 131 132 // mock ContainerKill failed 133 cli.EXPECT().ContainerKill(gomock.Any(), gomock.Any(), gomock.Any()). 134 Return(cfgcore.MakeError("failed to kill docker container!")) 135 // mock ContainerKill success 136 cli.EXPECT().ContainerKill(gomock.Any(), gomock.Any(), gomock.Any()). 137 Return(nil).AnyTimes() 138 139 require.ErrorContains(t, 140 docker.Kill(context.Background(), []string{"76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55"}, "", nil), 141 "docker service not ready!") 142 require.Nil(t, docker.Kill(context.Background(), []string{"76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55"}, "", nil)) 143 require.Nil(t, docker.Kill(context.Background(), []string{"76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55"}, "", nil)) 144 require.ErrorContains(t, 145 docker.Kill(context.Background(), []string{"76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55"}, "", nil), 146 "failed to kill docker container") 147 require.Nil(t, docker.Kill(context.Background(), []string{"76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55"}, "", nil)) 148 } 149 150 func TestContainerdContainerKill(t *testing.T) { 151 mockCtrl := gomock.NewController(t) 152 defer mockCtrl.Finish() 153 criCli := mocks.NewMockRuntimeServiceClient(mockCtrl) 154 155 containerd := containerdContainer{ 156 logger: zapLog.Sugar(), 157 backendRuntime: criCli, 158 } 159 160 // ctx context.Context, in *ListContainersRequest, opts ...grpc.CallOption 161 // mock ListContainers failed 162 criCli.EXPECT().ListContainers(gomock.Any(), gomock.Any()). 163 Return(nil, cfgcore.MakeError("failed to list containers!")) 164 165 // mock not exist 166 criCli.EXPECT().ListContainers(gomock.Any(), gomock.Any()). 167 Return(nil, nil) 168 169 // mock exited 170 criCli.EXPECT().ListContainers(gomock.Any(), gomock.Any()). 171 Return(&runtimeapi.ListContainersResponse{Containers: []*runtimeapi.Container{{ 172 Id: "76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55", 173 State: runtimeapi.ContainerState_CONTAINER_EXITED, 174 }}}, nil) 175 176 // mock running 177 criCli.EXPECT().ListContainers(gomock.Any(), gomock.Any()). 178 Return(&runtimeapi.ListContainersResponse{Containers: []*runtimeapi.Container{{ 179 Id: "76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55", 180 State: runtimeapi.ContainerState_CONTAINER_RUNNING, 181 }}}, nil).AnyTimes() 182 183 // mock stop failed 184 criCli.EXPECT().StopContainer(gomock.Any(), gomock.Any()). 185 Return(nil, cfgcore.MakeError("failed to stop container!")) 186 criCli.EXPECT().StopContainer(gomock.Any(), gomock.Any()). 187 Return(&runtimeapi.StopContainerResponse{}, nil) 188 189 require.ErrorContains(t, 190 containerd.Kill(context.Background(), []string{"76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55"}, "", nil), 191 "failed to list containers!") 192 193 require.Nil(t, containerd.Kill(context.Background(), []string{"76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55"}, "", nil)) 194 require.Nil(t, containerd.Kill(context.Background(), []string{"76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55"}, "", nil)) 195 196 require.ErrorContains(t, 197 containerd.Kill(context.Background(), []string{"76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55"}, "", nil), 198 "failed to stop container!") 199 require.Nil(t, containerd.Kill(context.Background(), []string{"76f9c2ae8cf47bfa43b97626e3c95045cb3b82c50019ab759801ab52e3acff55"}, "", nil)) 200 201 } 202 203 func TestAutoCheckCRIType(t *testing.T) { 204 tmpDir, err := os.MkdirTemp(os.TempDir(), "SocketFileTest-") 205 require.Nil(t, err) 206 defer os.RemoveAll(tmpDir) 207 208 var ( 209 testFile1 = filepath.Join(tmpDir, "file1.sock") 210 testFile2 = filepath.Join(tmpDir, "file2.sock") 211 testFile3 = filepath.Join(tmpDir, "file3.sock") 212 dockerFile = filepath.Join(tmpDir, "docker.sock") 213 ) 214 215 require.Equal(t, autoCheckCRIType([]string{testFile1, testFile2, testFile3}, dockerFile, zapLog.Sugar()), CRIType("")) 216 217 l, err := net.Listen("unix", dockerFile) 218 if err != nil { 219 t.Errorf("failed to create socket file: %s", dockerFile) 220 } 221 defer l.Close() 222 require.Equal(t, autoCheckCRIType([]string{testFile1, testFile2, testFile3}, dockerFile, zapLog.Sugar()), DockerType) 223 224 // mock grpc 225 { 226 listen, err := net.Listen("unix", testFile3) 227 if err != nil { 228 t.Fatalf("Error while listening. Err: %v", err) 229 } 230 defer listen.Close() 231 listenDone := make(chan struct{}) 232 dialDone := make(chan struct{}) 233 // mock grpc connection 234 go func() { 235 defer close(listenDone) 236 conn, err := listen.Accept() 237 if err != nil { 238 t.Errorf("failed to accepting. Err: %v", err) 239 return 240 } 241 framer := http2.NewFramer(conn, conn) 242 if err := framer.WriteSettings(http2.Setting{}); err != nil { 243 t.Errorf("failed to writing settings. Err: %v", err) 244 return 245 } 246 <-dialDone // wait for dialDone before closing connection 247 conn.Close() 248 }() 249 250 // for test 251 require.Equal(t, autoCheckCRIType([]string{testFile1, testFile2, testFile3}, dockerFile, zapLog.Sugar()), ContainerdType) 252 // close grpc mock 253 close(dialDone) 254 255 // wait grpc listen close 256 timeout := time.After(1 * time.Second) 257 select { 258 case <-timeout: 259 t.Fatal("timed out waiting for server to finish") 260 case <-listenDone: 261 } 262 } 263 } 264 265 func TestContainerdInit(t *testing.T) { 266 mockCtrl := gomock.NewController(t) 267 defer mockCtrl.Finish() 268 criCli := mocks.NewMockRuntimeServiceClient(mockCtrl) 269 270 containerd := containerdContainer{ 271 logger: zapLog.Sugar(), 272 backendRuntime: criCli, 273 runtimeEndpoint: "not_exist_point", 274 } 275 require.NotNil(t, containerd.Init(context.Background())) 276 } 277 278 func TestContainerdPingCRI(t *testing.T) { 279 mockCtrl := gomock.NewController(t) 280 defer mockCtrl.Finish() 281 criCli := mocks.NewMockRuntimeServiceClient(mockCtrl) 282 283 criCli.EXPECT().Status(gomock.Any(), gomock.Any()). 284 Return(nil, cfgcore.MakeError("failed to ping CRI!")) 285 criCli.EXPECT().Status(gomock.Any(), gomock.Any()). 286 Return(&runtimeapi.StatusResponse{ 287 Status: &runtimeapi.RuntimeStatus{}, 288 }, nil) 289 290 containerd := containerdContainer{ 291 logger: zapLog.Sugar(), 292 backendRuntime: criCli, 293 } 294 295 require.NotNil(t, containerd.pingCRI(context.Background(), criCli)) 296 require.Nil(t, containerd.pingCRI(context.Background(), criCli)) 297 } 298 299 func TestDockerInit(t *testing.T) { 300 tmpDir, err := os.MkdirTemp(os.TempDir(), "SocketFileTest-") 301 require.Nil(t, err) 302 defer os.RemoveAll(tmpDir) 303 304 docker := &dockerContainer{ 305 logger: zapLog.Sugar(), 306 dockerEndpoint: filepath.Join(tmpDir, "docker.sock"), 307 } 308 require.NotNil(t, docker.Init(context.Background())) 309 }