github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/kata_agent.go (about) 1 // Copyright (c) 2017 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 package virtcontainers 7 8 import ( 9 "encoding/json" 10 "errors" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "path/filepath" 15 "strconv" 16 "strings" 17 "sync" 18 "syscall" 19 "time" 20 21 units "github.com/docker/go-units" 22 "github.com/gogo/protobuf/proto" 23 aTypes "github.com/kata-containers/agent/pkg/types" 24 kataclient "github.com/kata-containers/agent/protocols/client" 25 "github.com/kata-containers/agent/protocols/grpc" 26 "github.com/kata-containers/runtime/virtcontainers/device/api" 27 "github.com/kata-containers/runtime/virtcontainers/device/config" 28 persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api" 29 vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" 30 vccgroups "github.com/kata-containers/runtime/virtcontainers/pkg/cgroups" 31 ns "github.com/kata-containers/runtime/virtcontainers/pkg/nsenter" 32 "github.com/kata-containers/runtime/virtcontainers/pkg/rootless" 33 vcTypes "github.com/kata-containers/runtime/virtcontainers/pkg/types" 34 "github.com/kata-containers/runtime/virtcontainers/pkg/uuid" 35 "github.com/kata-containers/runtime/virtcontainers/store" 36 "github.com/kata-containers/runtime/virtcontainers/types" 37 "github.com/kata-containers/runtime/virtcontainers/utils" 38 "github.com/opencontainers/runtime-spec/specs-go" 39 opentracing "github.com/opentracing/opentracing-go" 40 "github.com/sirupsen/logrus" 41 "github.com/vishvananda/netlink" 42 "golang.org/x/net/context" 43 "golang.org/x/sys/unix" 44 golangGrpc "google.golang.org/grpc" 45 "google.golang.org/grpc/codes" 46 grpcStatus "google.golang.org/grpc/status" 47 ) 48 49 const ( 50 // KataEphemeralDevType creates a tmpfs backed volume for sharing files between containers. 51 KataEphemeralDevType = "ephemeral" 52 53 // KataLocalDevType creates a local directory inside the VM for sharing files between 54 // containers. 55 KataLocalDevType = "local" 56 57 // path to vfio devices 58 vfioPath = "/dev/vfio/" 59 60 agentPidEnv = "KATA_AGENT_PIDNS" 61 ) 62 63 var ( 64 checkRequestTimeout = 30 * time.Second 65 defaultRequestTimeout = 60 * time.Second 66 errorMissingProxy = errors.New("Missing proxy pointer") 67 errorMissingOCISpec = errors.New("Missing OCI specification") 68 defaultKataHostSharedDir = "/run/kata-containers/shared/sandboxes/" 69 defaultKataGuestSharedDir = "/run/kata-containers/shared/containers/" 70 mountGuestTag = "kataShared" 71 defaultKataGuestSandboxDir = "/run/kata-containers/sandbox/" 72 type9pFs = "9p" 73 typeVirtioFS = "virtiofs" 74 typeVirtioFSNoCache = "none" 75 kata9pDevType = "9p" 76 kataMmioBlkDevType = "mmioblk" 77 kataBlkDevType = "blk" 78 kataBlkCCWDevType = "blk-ccw" 79 kataSCSIDevType = "scsi" 80 kataNvdimmDevType = "nvdimm" 81 kataVirtioFSDevType = "virtio-fs" 82 sharedDir9pOptions = []string{"trans=virtio,version=9p2000.L,cache=mmap", "nodev"} 83 sharedDirVirtioFSOptions = []string{} 84 sharedDirVirtioFSDaxOptions = "dax" 85 shmDir = "shm" 86 kataEphemeralDevType = "ephemeral" 87 defaultEphemeralPath = filepath.Join(defaultKataGuestSandboxDir, kataEphemeralDevType) 88 grpcMaxDataSize = int64(1024 * 1024) 89 localDirOptions = []string{"mode=0777"} 90 maxHostnameLen = 64 91 GuestDNSFile = "/etc/resolv.conf" 92 ) 93 94 const ( 95 agentTraceModeDynamic = "dynamic" 96 agentTraceModeStatic = "static" 97 agentTraceTypeIsolated = "isolated" 98 agentTraceTypeCollated = "collated" 99 100 defaultAgentTraceMode = agentTraceModeDynamic 101 defaultAgentTraceType = agentTraceTypeIsolated 102 ) 103 104 const ( 105 grpcCheckRequest = "grpc.CheckRequest" 106 grpcExecProcessRequest = "grpc.ExecProcessRequest" 107 grpcCreateSandboxRequest = "grpc.CreateSandboxRequest" 108 grpcDestroySandboxRequest = "grpc.DestroySandboxRequest" 109 grpcCreateContainerRequest = "grpc.CreateContainerRequest" 110 grpcStartContainerRequest = "grpc.StartContainerRequest" 111 grpcRemoveContainerRequest = "grpc.RemoveContainerRequest" 112 grpcSignalProcessRequest = "grpc.SignalProcessRequest" 113 grpcUpdateRoutesRequest = "grpc.UpdateRoutesRequest" 114 grpcUpdateInterfaceRequest = "grpc.UpdateInterfaceRequest" 115 grpcListInterfacesRequest = "grpc.ListInterfacesRequest" 116 grpcListRoutesRequest = "grpc.ListRoutesRequest" 117 grpcAddARPNeighborsRequest = "grpc.AddARPNeighborsRequest" 118 grpcOnlineCPUMemRequest = "grpc.OnlineCPUMemRequest" 119 grpcListProcessesRequest = "grpc.ListProcessesRequest" 120 grpcUpdateContainerRequest = "grpc.UpdateContainerRequest" 121 grpcWaitProcessRequest = "grpc.WaitProcessRequest" 122 grpcTtyWinResizeRequest = "grpc.TtyWinResizeRequest" 123 grpcWriteStreamRequest = "grpc.WriteStreamRequest" 124 grpcCloseStdinRequest = "grpc.CloseStdinRequest" 125 grpcStatsContainerRequest = "grpc.StatsContainerRequest" 126 grpcPauseContainerRequest = "grpc.PauseContainerRequest" 127 grpcResumeContainerRequest = "grpc.ResumeContainerRequest" 128 grpcReseedRandomDevRequest = "grpc.ReseedRandomDevRequest" 129 grpcGuestDetailsRequest = "grpc.GuestDetailsRequest" 130 grpcMemHotplugByProbeRequest = "grpc.MemHotplugByProbeRequest" 131 grpcCopyFileRequest = "grpc.CopyFileRequest" 132 grpcSetGuestDateTimeRequest = "grpc.SetGuestDateTimeRequest" 133 grpcStartTracingRequest = "grpc.StartTracingRequest" 134 grpcStopTracingRequest = "grpc.StopTracingRequest" 135 grpcGetOOMEventRequest = "grpc.GetOOMEventRequest" 136 ) 137 138 // The function is declared this way for mocking in unit tests 139 var kataHostSharedDir = func() string { 140 if rootless.IsRootless() { 141 // filepath.Join removes trailing slashes, but it is necessary for mounting 142 return filepath.Join(rootless.GetRootlessDir(), defaultKataHostSharedDir) + "/" 143 } 144 return defaultKataHostSharedDir 145 } 146 147 func getPagesizeFromOpt(fsOpts []string) string { 148 //example options array: "rw", "relatime", "seclabel", "pagesize=2M" 149 150 for _, opt := range fsOpts { 151 if strings.HasPrefix(opt, "pagesize=") { 152 return strings.TrimPrefix(opt, "pagesize=") 153 } 154 } 155 return "" 156 } 157 158 // Shared path handling: 159 // 1. create two directories for each sandbox: 160 // -. /run/kata-containers/shared/sandboxes/$sbx_id/mounts/, a directory to hold all host/guest shared mounts 161 // -. /run/kata-containers/shared/sandboxes/$sbx_id/shared/, a host/guest shared directory (9pfs/virtiofs source dir) 162 // 163 // 2. /run/kata-containers/shared/sandboxes/$sbx_id/mounts/ is bind mounted readonly to /run/kata-containers/shared/sandboxes/$sbx_id/shared/, so guest cannot modify it 164 // 165 // 3. host-guest shared files/directories are mounted one-level under /run/kata-containers/shared/sandboxes/$sbx_id/mounts/ and thus present to guest at one level under /run/kata-containers/shared/sandboxes/$sbx_id/shared/ 166 func getSharePath(id string) string { 167 return filepath.Join(kataHostSharedDir(), id, "shared") 168 } 169 170 func getMountPath(id string) string { 171 return filepath.Join(kataHostSharedDir(), id, "mounts") 172 } 173 174 func getSandboxPath(id string) string { 175 return filepath.Join(kataHostSharedDir(), id) 176 } 177 178 // The function is declared this way for mocking in unit tests 179 var kataGuestSharedDir = func() string { 180 if rootless.IsRootless() { 181 // filepath.Join removes trailing slashes, but it is necessary for mounting 182 return filepath.Join(rootless.GetRootlessDir(), defaultKataGuestSharedDir) + "/" 183 } 184 return defaultKataGuestSharedDir 185 } 186 187 // The function is declared this way for mocking in unit tests 188 var kataGuestSandboxDir = func() string { 189 if rootless.IsRootless() { 190 // filepath.Join removes trailing slashes, but it is necessary for mounting 191 return filepath.Join(rootless.GetRootlessDir(), defaultKataGuestSandboxDir) + "/" 192 } 193 return defaultKataGuestSandboxDir 194 } 195 196 var kataGuestSandboxStorageDir = func() string { 197 return filepath.Join(defaultKataGuestSandboxDir, "storage") 198 } 199 200 func ephemeralPath() string { 201 if rootless.IsRootless() { 202 return filepath.Join(kataGuestSandboxDir(), kataEphemeralDevType) 203 } 204 return defaultEphemeralPath 205 } 206 207 // KataAgentConfig is a structure storing information needed 208 // to reach the Kata Containers agent. 209 type KataAgentConfig struct { 210 LongLiveConn bool 211 UseVSock bool 212 Debug bool 213 Trace bool 214 ContainerPipeSize uint32 215 TraceMode string 216 TraceType string 217 KernelModules []string 218 } 219 220 // KataAgentState is the structure describing the data stored from this 221 // agent implementation. 222 type KataAgentState struct { 223 ProxyPid int 224 URL string 225 } 226 227 type kataAgent struct { 228 shim shim 229 proxy proxy 230 231 // lock protects the client pointer 232 sync.Mutex 233 client *kataclient.AgentClient 234 235 reqHandlers map[string]reqFunc 236 state KataAgentState 237 keepConn bool 238 proxyBuiltIn bool 239 dynamicTracing bool 240 dead bool 241 kmodules []string 242 243 vmSocket interface{} 244 ctx context.Context 245 } 246 247 func (k *kataAgent) trace(name string) (opentracing.Span, context.Context) { 248 if k.ctx == nil { 249 k.Logger().WithField("type", "bug").Error("trace called before context set") 250 k.ctx = context.Background() 251 } 252 253 span, ctx := opentracing.StartSpanFromContext(k.ctx, name) 254 255 span.SetTag("subsystem", "agent") 256 span.SetTag("type", "kata") 257 258 return span, ctx 259 } 260 261 func (k *kataAgent) Logger() *logrus.Entry { 262 return virtLog.WithField("subsystem", "kata_agent") 263 } 264 265 func (k *kataAgent) longLiveConn() bool { 266 return k.keepConn 267 } 268 269 // KataAgentSetDefaultTraceConfigOptions validates agent trace options and 270 // sets defaults. 271 func KataAgentSetDefaultTraceConfigOptions(config *KataAgentConfig) error { 272 if !config.Trace { 273 return nil 274 } 275 276 switch config.TraceMode { 277 case agentTraceModeDynamic: 278 case agentTraceModeStatic: 279 case "": 280 config.TraceMode = defaultAgentTraceMode 281 default: 282 return fmt.Errorf("invalid kata agent trace mode: %q (need %q or %q)", config.TraceMode, agentTraceModeDynamic, agentTraceModeStatic) 283 } 284 285 switch config.TraceType { 286 case agentTraceTypeIsolated: 287 case agentTraceTypeCollated: 288 case "": 289 config.TraceType = defaultAgentTraceType 290 default: 291 return fmt.Errorf("invalid kata agent trace type: %q (need %q or %q)", config.TraceType, agentTraceTypeIsolated, agentTraceTypeCollated) 292 } 293 294 return nil 295 } 296 297 // KataAgentKernelParams returns a list of Kata Agent specific kernel 298 // parameters. 299 func KataAgentKernelParams(config KataAgentConfig) []Param { 300 var params []Param 301 302 if config.Debug { 303 params = append(params, Param{Key: "agent.log", Value: "debug"}) 304 } 305 306 if config.Trace && config.TraceMode == agentTraceModeStatic { 307 params = append(params, Param{Key: "agent.trace", Value: config.TraceType}) 308 } 309 310 if config.ContainerPipeSize > 0 { 311 containerPipeSize := strconv.FormatUint(uint64(config.ContainerPipeSize), 10) 312 params = append(params, Param{Key: vcAnnotations.ContainerPipeSizeKernelParam, Value: containerPipeSize}) 313 } 314 315 return params 316 } 317 318 func (k *kataAgent) handleTraceSettings(config KataAgentConfig) bool { 319 if !config.Trace { 320 return false 321 } 322 323 disableVMShutdown := false 324 325 switch config.TraceMode { 326 case agentTraceModeStatic: 327 disableVMShutdown = true 328 case agentTraceModeDynamic: 329 k.dynamicTracing = true 330 } 331 332 return disableVMShutdown 333 } 334 335 func (k *kataAgent) init(ctx context.Context, sandbox *Sandbox, config interface{}) (disableVMShutdown bool, err error) { 336 // save 337 k.ctx = sandbox.ctx 338 339 span, _ := k.trace("init") 340 defer span.Finish() 341 342 switch c := config.(type) { 343 case KataAgentConfig: 344 disableVMShutdown = k.handleTraceSettings(c) 345 k.keepConn = c.LongLiveConn 346 k.kmodules = c.KernelModules 347 default: 348 return false, vcTypes.ErrInvalidConfigType 349 } 350 351 k.proxy, err = newProxy(sandbox.config.ProxyType) 352 if err != nil { 353 return false, err 354 } 355 356 k.shim, err = newShim(sandbox.config.ShimType) 357 if err != nil { 358 return false, err 359 } 360 361 k.proxyBuiltIn = isProxyBuiltIn(sandbox.config.ProxyType) 362 363 // Fetch agent runtime info. 364 if useOldStore(sandbox.ctx) { 365 if err := sandbox.store.Load(store.Agent, &k.state); err != nil { 366 k.Logger().Debug("Could not retrieve anything from storage") 367 } 368 } 369 return disableVMShutdown, nil 370 } 371 372 func (k *kataAgent) agentURL() (string, error) { 373 switch s := k.vmSocket.(type) { 374 case types.Socket: 375 return s.HostPath, nil 376 case types.VSock: 377 return s.String(), nil 378 case types.HybridVSock: 379 return s.String(), nil 380 default: 381 return "", fmt.Errorf("Invalid socket type") 382 } 383 } 384 385 func (k *kataAgent) capabilities() types.Capabilities { 386 var caps types.Capabilities 387 388 // add all capabilities supported by agent 389 caps.SetBlockDeviceSupport() 390 391 return caps 392 } 393 394 func (k *kataAgent) internalConfigure(h hypervisor, id string, builtin bool, config interface{}) error { 395 var err error 396 if config != nil { 397 switch c := config.(type) { 398 case KataAgentConfig: 399 if k.vmSocket, err = h.generateSocket(id, c.UseVSock); err != nil { 400 return err 401 } 402 k.keepConn = c.LongLiveConn 403 default: 404 return vcTypes.ErrInvalidConfigType 405 } 406 } 407 408 if builtin { 409 k.proxyBuiltIn = true 410 } 411 412 return nil 413 } 414 415 func (k *kataAgent) configure(h hypervisor, id, sharePath string, builtin bool, config interface{}) error { 416 err := k.internalConfigure(h, id, builtin, config) 417 if err != nil { 418 return err 419 } 420 421 switch s := k.vmSocket.(type) { 422 case types.Socket: 423 err = h.addDevice(s, serialPortDev) 424 if err != nil { 425 return err 426 } 427 case types.VSock: 428 if err = h.addDevice(s, vSockPCIDev); err != nil { 429 return err 430 } 431 case types.HybridVSock: 432 err = h.addDevice(s, hybridVirtioVsockDev) 433 if err != nil { 434 return err 435 } 436 default: 437 return vcTypes.ErrInvalidConfigType 438 } 439 440 // Neither create shared directory nor add 9p device if hypervisor 441 // doesn't support filesystem sharing. 442 caps := h.capabilities() 443 if !caps.IsFsSharingSupported() { 444 return nil 445 } 446 447 // Create shared directory and add the shared volume if filesystem sharing is supported. 448 // This volume contains all bind mounted container bundles. 449 sharedVolume := types.Volume{ 450 MountTag: mountGuestTag, 451 HostPath: sharePath, 452 } 453 454 if err = os.MkdirAll(sharedVolume.HostPath, DirMode); err != nil { 455 return err 456 } 457 458 return h.addDevice(sharedVolume, fsDev) 459 } 460 461 func (k *kataAgent) configureFromGrpc(h hypervisor, id string, builtin bool, config interface{}) error { 462 return k.internalConfigure(h, id, builtin, config) 463 } 464 465 func (k *kataAgent) setupSharedPath(sandbox *Sandbox) error { 466 // create shared path structure 467 sharePath := getSharePath(sandbox.id) 468 mountPath := getMountPath(sandbox.id) 469 if err := os.MkdirAll(sharePath, DirMode); err != nil { 470 return err 471 } 472 if err := os.MkdirAll(mountPath, DirMode); err != nil { 473 return err 474 } 475 476 // slave mount so that future mountpoints under mountPath are shown in sharePath as well 477 if err := bindMount(context.Background(), mountPath, sharePath, true, "slave"); err != nil { 478 return err 479 } 480 481 return nil 482 } 483 484 func (k *kataAgent) createSandbox(sandbox *Sandbox) error { 485 span, _ := k.trace("createSandbox") 486 defer span.Finish() 487 488 if err := k.setupSharedPath(sandbox); err != nil { 489 return err 490 } 491 return k.configure(sandbox.hypervisor, sandbox.id, getSharePath(sandbox.id), k.proxyBuiltIn, sandbox.config.AgentConfig) 492 } 493 494 func cmdToKataProcess(cmd types.Cmd) (process *grpc.Process, err error) { 495 var i uint64 496 var extraGids []uint32 497 498 // Number of bits used to store user+group values in 499 // the gRPC "User" type. 500 const grpcUserBits = 32 501 502 // User can contain only the "uid" or it can contain "uid:gid". 503 parsedUser := strings.Split(cmd.User, ":") 504 if len(parsedUser) > 2 { 505 return nil, fmt.Errorf("cmd.User %q format is wrong", cmd.User) 506 } 507 508 i, err = strconv.ParseUint(parsedUser[0], 10, grpcUserBits) 509 if err != nil { 510 return nil, err 511 } 512 513 uid := uint32(i) 514 515 var gid uint32 516 if len(parsedUser) > 1 { 517 i, err = strconv.ParseUint(parsedUser[1], 10, grpcUserBits) 518 if err != nil { 519 return nil, err 520 } 521 522 gid = uint32(i) 523 } 524 525 if cmd.PrimaryGroup != "" { 526 i, err = strconv.ParseUint(cmd.PrimaryGroup, 10, grpcUserBits) 527 if err != nil { 528 return nil, err 529 } 530 531 gid = uint32(i) 532 } 533 534 for _, g := range cmd.SupplementaryGroups { 535 var extraGid uint64 536 537 extraGid, err = strconv.ParseUint(g, 10, grpcUserBits) 538 if err != nil { 539 return nil, err 540 } 541 542 extraGids = append(extraGids, uint32(extraGid)) 543 } 544 545 process = &grpc.Process{ 546 Terminal: cmd.Interactive, 547 User: grpc.User{ 548 UID: uid, 549 GID: gid, 550 AdditionalGids: extraGids, 551 }, 552 Args: cmd.Args, 553 Env: cmdEnvsToStringSlice(cmd.Envs), 554 Cwd: cmd.WorkDir, 555 } 556 557 return process, nil 558 } 559 560 func cmdEnvsToStringSlice(ev []types.EnvVar) []string { 561 var env []string 562 563 for _, e := range ev { 564 pair := []string{e.Var, e.Value} 565 env = append(env, strings.Join(pair, "=")) 566 } 567 568 return env 569 } 570 571 func (k *kataAgent) exec(sandbox *Sandbox, c Container, cmd types.Cmd) (*Process, error) { 572 span, _ := k.trace("exec") 573 defer span.Finish() 574 575 var kataProcess *grpc.Process 576 577 kataProcess, err := cmdToKataProcess(cmd) 578 if err != nil { 579 return nil, err 580 } 581 582 req := &grpc.ExecProcessRequest{ 583 ContainerId: c.id, 584 ExecId: uuid.Generate().String(), 585 Process: kataProcess, 586 } 587 588 if _, err := k.sendReq(req); err != nil { 589 return nil, err 590 } 591 592 enterNSList := []ns.Namespace{ 593 { 594 PID: c.process.Pid, 595 Type: ns.NSTypeNet, 596 }, 597 { 598 PID: c.process.Pid, 599 Type: ns.NSTypePID, 600 }, 601 } 602 603 return prepareAndStartShim(sandbox, k.shim, c.id, req.ExecId, 604 k.state.URL, "", cmd, []ns.NSType{}, enterNSList) 605 } 606 607 func (k *kataAgent) updateInterface(ifc *vcTypes.Interface) (*vcTypes.Interface, error) { 608 // send update interface request 609 ifcReq := &grpc.UpdateInterfaceRequest{ 610 Interface: k.convertToKataAgentInterface(ifc), 611 } 612 resultingInterface, err := k.sendReq(ifcReq) 613 if err != nil { 614 k.Logger().WithFields(logrus.Fields{ 615 "interface-requested": fmt.Sprintf("%+v", ifc), 616 "resulting-interface": fmt.Sprintf("%+v", resultingInterface), 617 }).WithError(err).Error("update interface request failed") 618 } 619 if resultInterface, ok := resultingInterface.(*vcTypes.Interface); ok { 620 return resultInterface, err 621 } 622 return nil, err 623 } 624 625 func (k *kataAgent) updateInterfaces(interfaces []*vcTypes.Interface) error { 626 for _, ifc := range interfaces { 627 if _, err := k.updateInterface(ifc); err != nil { 628 return err 629 } 630 } 631 return nil 632 } 633 634 func (k *kataAgent) updateRoutes(routes []*vcTypes.Route) ([]*vcTypes.Route, error) { 635 if routes != nil { 636 routesReq := &grpc.UpdateRoutesRequest{ 637 Routes: &grpc.Routes{ 638 Routes: k.convertToKataAgentRoutes(routes), 639 }, 640 } 641 resultingRoutes, err := k.sendReq(routesReq) 642 if err != nil { 643 k.Logger().WithFields(logrus.Fields{ 644 "routes-requested": fmt.Sprintf("%+v", routes), 645 "resulting-routes": fmt.Sprintf("%+v", resultingRoutes), 646 }).WithError(err).Error("update routes request failed") 647 } 648 resultRoutes, ok := resultingRoutes.(*grpc.Routes) 649 if ok && resultRoutes != nil { 650 return k.convertToRoutes(resultRoutes.Routes), err 651 } 652 return nil, err 653 } 654 return nil, nil 655 } 656 657 func (k *kataAgent) addARPNeighbors(neighs []*vcTypes.ARPNeighbor) error { 658 if neighs != nil { 659 neighsReq := &grpc.AddARPNeighborsRequest{ 660 Neighbors: &grpc.ARPNeighbors{ 661 ARPNeighbors: k.convertToKataAgentNeighbors(neighs), 662 }, 663 } 664 _, err := k.sendReq(neighsReq) 665 if err != nil { 666 if grpcStatus.Convert(err).Code() == codes.Unimplemented { 667 k.Logger().WithFields(logrus.Fields{ 668 "arpneighbors-requested": fmt.Sprintf("%+v", neighs), 669 }).Warn("add ARP neighbors request failed due to old agent, please upgrade Kata Containers image version") 670 return nil 671 } 672 k.Logger().WithFields(logrus.Fields{ 673 "arpneighbors-requested": fmt.Sprintf("%+v", neighs), 674 }).WithError(err).Error("add ARP neighbors request failed") 675 } 676 return err 677 } 678 return nil 679 } 680 681 func (k *kataAgent) listInterfaces() ([]*vcTypes.Interface, error) { 682 req := &grpc.ListInterfacesRequest{} 683 resultingInterfaces, err := k.sendReq(req) 684 if err != nil { 685 return nil, err 686 } 687 resultInterfaces, ok := resultingInterfaces.(*grpc.Interfaces) 688 if !ok { 689 return nil, fmt.Errorf("Unexpected type %T for interfaces", resultingInterfaces) 690 } 691 return k.convertToInterfaces(resultInterfaces.Interfaces) 692 } 693 694 func (k *kataAgent) listRoutes() ([]*vcTypes.Route, error) { 695 req := &grpc.ListRoutesRequest{} 696 resultingRoutes, err := k.sendReq(req) 697 if err != nil { 698 return nil, err 699 } 700 resultRoutes, ok := resultingRoutes.(*grpc.Routes) 701 if !ok { 702 return nil, fmt.Errorf("Unexpected type %T for routes", resultingRoutes) 703 } 704 return k.convertToRoutes(resultRoutes.Routes), nil 705 } 706 707 func (k *kataAgent) startProxy(sandbox *Sandbox) error { 708 span, _ := k.trace("startProxy") 709 defer span.Finish() 710 711 var err error 712 var agentURL string 713 714 if k.proxy == nil { 715 return errorMissingProxy 716 } 717 718 if k.proxy.consoleWatched() { 719 return nil 720 } 721 722 if k.state.URL != "" { 723 // For keepConn case, when k.state.URL isn't nil, it means shimv2 had disconnected from 724 // sandbox and try to relaunch sandbox again. Here it needs to start proxy again to watch 725 // the hypervisor console. 726 if k.keepConn { 727 agentURL = k.state.URL 728 } else { 729 k.Logger().WithFields(logrus.Fields{ 730 "sandbox": sandbox.id, 731 "proxy-pid": k.state.ProxyPid, 732 "proxy-url": k.state.URL, 733 }).Infof("proxy already started") 734 return nil 735 } 736 } else { 737 // Get agent socket path to provide it to the proxy. 738 agentURL, err = k.agentURL() 739 if err != nil { 740 return err 741 } 742 } 743 744 consoleURL, err := sandbox.hypervisor.getSandboxConsole(sandbox.id) 745 if err != nil { 746 return err 747 } 748 749 proxyParams := proxyParams{ 750 id: sandbox.id, 751 hid: getHypervisorPid(sandbox.hypervisor), 752 path: sandbox.config.ProxyConfig.Path, 753 agentURL: agentURL, 754 consoleURL: consoleURL, 755 logger: k.Logger().WithField("sandbox", sandbox.id), 756 // Disable debug so proxy doesn't read console if we want to 757 // debug the agent console ourselves. 758 debug: sandbox.config.ProxyConfig.Debug && 759 !k.hasAgentDebugConsole(sandbox), 760 } 761 762 // Start the proxy here 763 pid, uri, err := k.proxy.start(proxyParams) 764 if err != nil { 765 return err 766 } 767 768 // If error occurs after kata-proxy process start, 769 // then rollback to kill kata-proxy process 770 defer func() { 771 if err != nil { 772 k.proxy.stop(pid) 773 } 774 }() 775 776 // Fill agent state with proxy information, and store them. 777 if err = k.setProxy(sandbox, k.proxy, pid, uri); err != nil { 778 return err 779 } 780 781 k.Logger().WithFields(logrus.Fields{ 782 "sandbox": sandbox.id, 783 "proxy-pid": pid, 784 "proxy-url": uri, 785 }).Info("proxy started") 786 787 return nil 788 } 789 790 func (k *kataAgent) getAgentURL() (string, error) { 791 return k.agentURL() 792 } 793 794 func (k *kataAgent) reuseAgent(agent agent) error { 795 a, ok := agent.(*kataAgent) 796 if !ok { 797 return fmt.Errorf("Bug: get a wrong type of agent") 798 } 799 800 k.installReqFunc(a.client) 801 k.client = a.client 802 return nil 803 } 804 805 func (k *kataAgent) setProxy(sandbox *Sandbox, proxy proxy, pid int, url string) error { 806 if url == "" { 807 var err error 808 if url, err = k.agentURL(); err != nil { 809 return err 810 } 811 } 812 813 // Are we setting the same proxy again? 814 if k.proxy != nil && k.state.URL != "" && k.state.URL != url { 815 k.proxy.stop(k.state.ProxyPid) 816 } 817 818 k.proxy = proxy 819 k.state.ProxyPid = pid 820 k.state.URL = url 821 return nil 822 } 823 824 func (k *kataAgent) setProxyFromGrpc(proxy proxy, pid int, url string) { 825 k.proxy = proxy 826 k.state.ProxyPid = pid 827 k.state.URL = url 828 } 829 830 func (k *kataAgent) getDNS(sandbox *Sandbox) ([]string, error) { 831 ociSpec := sandbox.GetPatchedOCISpec() 832 if ociSpec == nil { 833 k.Logger().Debug("Sandbox OCI spec not found. Sandbox DNS will not be set.") 834 return nil, nil 835 } 836 837 ociMounts := ociSpec.Mounts 838 839 for _, m := range ociMounts { 840 if m.Destination == GuestDNSFile { 841 content, err := ioutil.ReadFile(m.Source) 842 if err != nil { 843 return nil, fmt.Errorf("Could not read file %s: %s", m.Source, err) 844 } 845 dns := strings.Split(string(content), "\n") 846 return dns, nil 847 848 } 849 } 850 k.Logger().Debug("DNS file not present in ociMounts. Sandbox DNS will not be set.") 851 return nil, nil 852 } 853 854 func (k *kataAgent) startSandbox(sandbox *Sandbox) error { 855 span, _ := k.trace("startSandbox") 856 defer span.Finish() 857 858 err := k.startProxy(sandbox) 859 if err != nil { 860 return err 861 } 862 863 defer func() { 864 if err != nil { 865 k.proxy.stop(k.state.ProxyPid) 866 } 867 }() 868 hostname := sandbox.config.Hostname 869 if len(hostname) > maxHostnameLen { 870 hostname = hostname[:maxHostnameLen] 871 } 872 873 dns, err := k.getDNS(sandbox) 874 if err != nil { 875 return err 876 } 877 878 // check grpc server is serving 879 if err = k.check(); err != nil { 880 return err 881 } 882 883 // 884 // Setup network interfaces and routes 885 // 886 interfaces, routes, neighs, err := generateVCNetworkStructures(sandbox.networkNS) 887 if err != nil { 888 return err 889 } 890 if err = k.updateInterfaces(interfaces); err != nil { 891 return err 892 } 893 if _, err = k.updateRoutes(routes); err != nil { 894 return err 895 } 896 if err = k.addARPNeighbors(neighs); err != nil { 897 return err 898 } 899 900 storages := setupStorages(sandbox) 901 902 kmodules := setupKernelModules(k.kmodules) 903 904 req := &grpc.CreateSandboxRequest{ 905 Hostname: hostname, 906 Dns: dns, 907 Storages: storages, 908 SandboxPidns: sandbox.sharePidNs, 909 SandboxId: sandbox.id, 910 GuestHookPath: sandbox.config.HypervisorConfig.GuestHookPath, 911 KernelModules: kmodules, 912 } 913 914 _, err = k.sendReq(req) 915 if err != nil { 916 return err 917 } 918 919 if k.dynamicTracing { 920 _, err = k.sendReq(&grpc.StartTracingRequest{}) 921 if err != nil { 922 return err 923 } 924 } 925 926 return nil 927 } 928 929 func setupKernelModules(kmodules []string) []*grpc.KernelModule { 930 modules := []*grpc.KernelModule{} 931 932 for _, m := range kmodules { 933 l := strings.Fields(strings.TrimSpace(m)) 934 if len(l) == 0 { 935 continue 936 } 937 938 module := &grpc.KernelModule{Name: l[0]} 939 modules = append(modules, module) 940 if len(l) == 1 { 941 continue 942 } 943 944 module.Parameters = append(module.Parameters, l[1:]...) 945 } 946 947 return modules 948 } 949 950 func setupStorages(sandbox *Sandbox) []*grpc.Storage { 951 storages := []*grpc.Storage{} 952 caps := sandbox.hypervisor.capabilities() 953 954 // append 9p shared volume to storages only if filesystem sharing is supported 955 if caps.IsFsSharingSupported() { 956 // We mount the shared directory in a predefined location 957 // in the guest. 958 // This is where at least some of the host config files 959 // (resolv.conf, etc...) and potentially all container 960 // rootfs will reside. 961 if sandbox.config.HypervisorConfig.SharedFS == config.VirtioFS { 962 // If virtio-fs uses either of the two cache options 'auto, always', 963 // the guest directory can be mounted with option 'dax' allowing it to 964 // directly map contents from the host. When set to 'none', the mount 965 // options should not contain 'dax' lest the virtio-fs daemon crashing 966 // with an invalid address reference. 967 if sandbox.config.HypervisorConfig.VirtioFSCache != typeVirtioFSNoCache { 968 // If virtio_fs_cache_size = 0, dax should not be used. 969 if sandbox.config.HypervisorConfig.VirtioFSCacheSize != 0 { 970 sharedDirVirtioFSOptions = append(sharedDirVirtioFSOptions, sharedDirVirtioFSDaxOptions) 971 } 972 } 973 sharedVolume := &grpc.Storage{ 974 Driver: kataVirtioFSDevType, 975 Source: mountGuestTag, 976 MountPoint: kataGuestSharedDir(), 977 Fstype: typeVirtioFS, 978 Options: sharedDirVirtioFSOptions, 979 } 980 981 storages = append(storages, sharedVolume) 982 } else { 983 sharedDir9pOptions = append(sharedDir9pOptions, fmt.Sprintf("msize=%d", sandbox.config.HypervisorConfig.Msize9p)) 984 985 sharedVolume := &grpc.Storage{ 986 Driver: kata9pDevType, 987 Source: mountGuestTag, 988 MountPoint: kataGuestSharedDir(), 989 Fstype: type9pFs, 990 Options: sharedDir9pOptions, 991 } 992 993 storages = append(storages, sharedVolume) 994 } 995 } 996 997 if sandbox.shmSize > 0 { 998 path := filepath.Join(kataGuestSandboxDir(), shmDir) 999 shmSizeOption := fmt.Sprintf("size=%d", sandbox.shmSize) 1000 1001 shmStorage := &grpc.Storage{ 1002 Driver: KataEphemeralDevType, 1003 MountPoint: path, 1004 Source: "shm", 1005 Fstype: "tmpfs", 1006 Options: []string{"noexec", "nosuid", "nodev", "mode=1777", shmSizeOption}, 1007 } 1008 1009 storages = append(storages, shmStorage) 1010 } 1011 1012 return storages 1013 } 1014 1015 func (k *kataAgent) stopSandbox(sandbox *Sandbox) error { 1016 span, _ := k.trace("stopSandbox") 1017 defer span.Finish() 1018 1019 if k.proxy == nil { 1020 return errorMissingProxy 1021 } 1022 1023 req := &grpc.DestroySandboxRequest{} 1024 1025 if _, err := k.sendReq(req); err != nil { 1026 return err 1027 } 1028 1029 if k.dynamicTracing { 1030 _, err := k.sendReq(&grpc.StopTracingRequest{}) 1031 if err != nil { 1032 return err 1033 } 1034 } 1035 1036 if err := k.proxy.stop(k.state.ProxyPid); err != nil { 1037 return err 1038 } 1039 1040 // clean up agent state 1041 k.state.ProxyPid = -1 1042 k.state.URL = "" 1043 return nil 1044 } 1045 1046 func (k *kataAgent) replaceOCIMountSource(spec *specs.Spec, guestMounts map[string]Mount) error { 1047 ociMounts := spec.Mounts 1048 1049 for index, m := range ociMounts { 1050 if guestMount, ok := guestMounts[m.Destination]; ok { 1051 k.Logger().Debugf("Replacing OCI mount (%s) source %s with %s", m.Destination, m.Source, guestMount.Source) 1052 ociMounts[index].Source = guestMount.Source 1053 } 1054 } 1055 1056 return nil 1057 } 1058 1059 func (k *kataAgent) removeIgnoredOCIMount(spec *specs.Spec, ignoredMounts map[string]Mount) error { 1060 var mounts []specs.Mount 1061 1062 for _, m := range spec.Mounts { 1063 if _, found := ignoredMounts[m.Source]; found { 1064 k.Logger().WithField("removed-mount", m.Source).Debug("Removing OCI mount") 1065 } else { 1066 mounts = append(mounts, m) 1067 } 1068 } 1069 1070 // Replace the OCI mounts with the updated list. 1071 spec.Mounts = mounts 1072 1073 return nil 1074 } 1075 1076 func (k *kataAgent) replaceOCIMountsForStorages(spec *specs.Spec, volumeStorages []*grpc.Storage) error { 1077 ociMounts := spec.Mounts 1078 var index int 1079 var m specs.Mount 1080 1081 for i, v := range volumeStorages { 1082 for index, m = range ociMounts { 1083 if m.Destination != v.MountPoint { 1084 continue 1085 } 1086 1087 // Create a temporary location to mount the Storage. Mounting to the correct location 1088 // will be handled by the OCI mount structure. 1089 filename := fmt.Sprintf("%s-%s", uuid.Generate().String(), filepath.Base(m.Destination)) 1090 path := filepath.Join(kataGuestSandboxStorageDir(), filename) 1091 1092 k.Logger().Debugf("Replacing OCI mount source (%s) with %s", m.Source, path) 1093 ociMounts[index].Source = path 1094 volumeStorages[i].MountPoint = path 1095 1096 break 1097 } 1098 if index == len(ociMounts) { 1099 return fmt.Errorf("OCI mount not found for block volume %s", v.MountPoint) 1100 } 1101 } 1102 return nil 1103 } 1104 1105 func (k *kataAgent) constraintGRPCSpec(grpcSpec *grpc.Spec, passSeccomp bool) { 1106 // Disable Hooks since they have been handled on the host and there is 1107 // no reason to send them to the agent. It would make no sense to try 1108 // to apply them on the guest. 1109 grpcSpec.Hooks = nil 1110 1111 // Pass seccomp only if disable_guest_seccomp is set to false in 1112 // configuration.toml and guest image is seccomp capable. 1113 if !passSeccomp { 1114 grpcSpec.Linux.Seccomp = nil 1115 } 1116 1117 // Disable SELinux inside of the virtual machine, the label will apply 1118 // to the KVM process 1119 if grpcSpec.Process.SelinuxLabel != "" { 1120 k.Logger().Info("SELinux label from config will be applied to the hypervisor process, not the VM workload") 1121 grpcSpec.Process.SelinuxLabel = "" 1122 } 1123 1124 // By now only CPU constraints are supported 1125 // Issue: https://github.com/kata-containers/runtime/issues/158 1126 // Issue: https://github.com/kata-containers/runtime/issues/204 1127 grpcSpec.Linux.Resources.Devices = nil 1128 grpcSpec.Linux.Resources.Pids = nil 1129 grpcSpec.Linux.Resources.BlockIO = nil 1130 grpcSpec.Linux.Resources.HugepageLimits = nil 1131 grpcSpec.Linux.Resources.Network = nil 1132 if grpcSpec.Linux.Resources.CPU != nil { 1133 grpcSpec.Linux.Resources.CPU.Cpus = "" 1134 grpcSpec.Linux.Resources.CPU.Mems = "" 1135 } 1136 1137 // There are three main reasons to do not apply systemd cgroups in the VM 1138 // - Initrd image doesn't have systemd. 1139 // - Nobody will be able to modify the resources of a specific container by using systemctl set-property. 1140 // - docker is not running in the VM. 1141 if vccgroups.IsSystemdCgroup(grpcSpec.Linux.CgroupsPath) { 1142 // Convert systemd cgroup to cgroupfs 1143 slice := strings.Split(grpcSpec.Linux.CgroupsPath, ":") 1144 // 0 - slice: system.slice 1145 // 1 - prefix: docker 1146 // 2 - name: abc123 1147 grpcSpec.Linux.CgroupsPath = filepath.Join("/", slice[1], slice[2]) 1148 } 1149 1150 // Disable network namespace since it is already handled on the host by 1151 // virtcontainers. The network is a complex part which cannot be simply 1152 // passed to the agent. 1153 // Every other namespaces's paths have to be emptied. This way, there 1154 // is no confusion from the agent, trying to find an existing namespace 1155 // on the guest. 1156 var tmpNamespaces []grpc.LinuxNamespace 1157 for _, ns := range grpcSpec.Linux.Namespaces { 1158 switch ns.Type { 1159 case specs.CgroupNamespace: 1160 case specs.NetworkNamespace: 1161 default: 1162 ns.Path = "" 1163 tmpNamespaces = append(tmpNamespaces, ns) 1164 } 1165 } 1166 grpcSpec.Linux.Namespaces = tmpNamespaces 1167 1168 // VFIO char device shouldn't not appear in the guest, 1169 // the device driver should handle it and determinate its group. 1170 var linuxDevices []grpc.LinuxDevice 1171 for _, dev := range grpcSpec.Linux.Devices { 1172 if dev.Type == "c" && strings.HasPrefix(dev.Path, vfioPath) { 1173 k.Logger().WithField("vfio-dev", dev.Path).Debug("removing vfio device from grpcSpec") 1174 continue 1175 } 1176 linuxDevices = append(linuxDevices, dev) 1177 } 1178 grpcSpec.Linux.Devices = linuxDevices 1179 } 1180 1181 func (k *kataAgent) handleShm(mounts []specs.Mount, sandbox *Sandbox) { 1182 for idx, mnt := range mounts { 1183 if mnt.Destination != "/dev/shm" { 1184 continue 1185 } 1186 1187 // If /dev/shm for a container is backed by an ephemeral volume, skip 1188 // bind-mounting it to the sandbox shm. 1189 // A later call to handleEphemeralStorage should take care of setting up /dev/shm correctly. 1190 if mnt.Type == KataEphemeralDevType { 1191 continue 1192 } 1193 1194 // A container shm mount is shared with sandbox shm mount. 1195 if sandbox.shmSize > 0 { 1196 mounts[idx].Type = "bind" 1197 mounts[idx].Options = []string{"rbind"} 1198 mounts[idx].Source = filepath.Join(kataGuestSandboxDir(), shmDir) 1199 k.Logger().WithField("shm-size", sandbox.shmSize).Info("Using sandbox shm") 1200 } else { 1201 // This should typically not happen, as a sandbox shm mount is always set up by the 1202 // upper stack. 1203 sizeOption := fmt.Sprintf("size=%d", DefaultShmSize) 1204 mounts[idx].Type = "tmpfs" 1205 mounts[idx].Source = "shm" 1206 mounts[idx].Options = []string{"noexec", "nosuid", "nodev", "mode=1777", sizeOption} 1207 k.Logger().WithField("shm-size", sizeOption).Info("Setting up a separate shm for container") 1208 } 1209 } 1210 } 1211 1212 func (k *kataAgent) appendBlockDevice(dev ContainerDevice, c *Container) *grpc.Device { 1213 device := c.sandbox.devManager.GetDeviceByID(dev.ID) 1214 1215 d, ok := device.GetDeviceInfo().(*config.BlockDrive) 1216 if !ok || d == nil { 1217 k.Logger().WithField("device", device).Error("malformed block drive") 1218 return nil 1219 } 1220 1221 if d.Pmem { 1222 // block drive is a persistent memory device that 1223 // was passed as volume (-v) not as device (--device). 1224 // It shouldn't be visible in the container 1225 return nil 1226 } 1227 1228 kataDevice := &grpc.Device{ 1229 ContainerPath: dev.ContainerPath, 1230 } 1231 1232 switch c.sandbox.config.HypervisorConfig.BlockDeviceDriver { 1233 case config.VirtioMmio: 1234 kataDevice.Type = kataMmioBlkDevType 1235 kataDevice.Id = d.VirtPath 1236 kataDevice.VmPath = d.VirtPath 1237 case config.VirtioBlockCCW: 1238 kataDevice.Type = kataBlkCCWDevType 1239 kataDevice.Id = d.DevNo 1240 case config.VirtioBlock: 1241 kataDevice.Type = kataBlkDevType 1242 kataDevice.Id = d.PCIPath.String() 1243 kataDevice.VmPath = d.VirtPath 1244 case config.VirtioSCSI: 1245 kataDevice.Type = kataSCSIDevType 1246 kataDevice.Id = d.SCSIAddr 1247 case config.Nvdimm: 1248 kataDevice.Type = kataNvdimmDevType 1249 kataDevice.VmPath = fmt.Sprintf("/dev/pmem%s", d.NvdimmID) 1250 } 1251 1252 return kataDevice 1253 } 1254 1255 func (k *kataAgent) appendVhostUserBlkDevice(dev ContainerDevice, c *Container) *grpc.Device { 1256 device := c.sandbox.devManager.GetDeviceByID(dev.ID) 1257 1258 d, ok := device.GetDeviceInfo().(*config.VhostUserDeviceAttrs) 1259 if !ok || d == nil { 1260 k.Logger().WithField("device", device).Error("malformed vhost-user-blk drive") 1261 return nil 1262 } 1263 1264 kataDevice := &grpc.Device{ 1265 ContainerPath: dev.ContainerPath, 1266 Type: kataBlkDevType, 1267 Id: d.PCIPath.String(), 1268 } 1269 1270 return kataDevice 1271 } 1272 1273 func (k *kataAgent) appendDevices(deviceList []*grpc.Device, c *Container) []*grpc.Device { 1274 var kataDevice *grpc.Device 1275 1276 for _, dev := range c.devices { 1277 device := c.sandbox.devManager.GetDeviceByID(dev.ID) 1278 if device == nil { 1279 k.Logger().WithField("device", dev.ID).Error("failed to find device by id") 1280 return nil 1281 } 1282 1283 switch device.DeviceType() { 1284 case config.DeviceBlock: 1285 kataDevice = k.appendBlockDevice(dev, c) 1286 case config.VhostUserBlk: 1287 kataDevice = k.appendVhostUserBlkDevice(dev, c) 1288 } 1289 1290 if kataDevice == nil { 1291 continue 1292 } 1293 1294 deviceList = append(deviceList, kataDevice) 1295 } 1296 1297 return deviceList 1298 } 1299 1300 // rollbackFailingContainerCreation rolls back important steps that might have 1301 // been performed before the container creation failed. 1302 // - Unmount container volumes. 1303 // - Unmount container rootfs. 1304 func (k *kataAgent) rollbackFailingContainerCreation(c *Container) { 1305 if c != nil { 1306 if err2 := c.unmountHostMounts(); err2 != nil { 1307 k.Logger().WithError(err2).Error("rollback failed unmountHostMounts()") 1308 } 1309 1310 if err2 := bindUnmountContainerRootfs(k.ctx, getMountPath(c.sandbox.id), c); err2 != nil { 1311 k.Logger().WithError(err2).Error("rollback failed bindUnmountContainerRootfs()") 1312 } 1313 } 1314 } 1315 1316 func (k *kataAgent) buildContainerRootfs(sandbox *Sandbox, c *Container, rootPathParent string) (*grpc.Storage, error) { 1317 if c.state.Fstype != "" && c.state.BlockDeviceID != "" { 1318 // The rootfs storage volume represents the container rootfs 1319 // mount point inside the guest. 1320 // It can be a block based device (when using block based container 1321 // overlay on the host) mount or a 9pfs one (for all other overlay 1322 // implementations). 1323 rootfs := &grpc.Storage{} 1324 1325 // This is a block based device rootfs. 1326 device := sandbox.devManager.GetDeviceByID(c.state.BlockDeviceID) 1327 if device == nil { 1328 k.Logger().WithField("device", c.state.BlockDeviceID).Error("failed to find device by id") 1329 return nil, fmt.Errorf("failed to find device by id %q", c.state.BlockDeviceID) 1330 } 1331 1332 blockDrive, ok := device.GetDeviceInfo().(*config.BlockDrive) 1333 if !ok || blockDrive == nil { 1334 k.Logger().Error("malformed block drive") 1335 return nil, fmt.Errorf("malformed block drive") 1336 } 1337 switch { 1338 case sandbox.config.HypervisorConfig.BlockDeviceDriver == config.VirtioMmio: 1339 rootfs.Driver = kataMmioBlkDevType 1340 rootfs.Source = blockDrive.VirtPath 1341 case sandbox.config.HypervisorConfig.BlockDeviceDriver == config.VirtioBlockCCW: 1342 rootfs.Driver = kataBlkCCWDevType 1343 rootfs.Source = blockDrive.DevNo 1344 case sandbox.config.HypervisorConfig.BlockDeviceDriver == config.VirtioBlock: 1345 rootfs.Driver = kataBlkDevType 1346 if blockDrive.PCIPath.IsNil() { 1347 rootfs.Source = blockDrive.VirtPath 1348 } else { 1349 rootfs.Source = blockDrive.PCIPath.String() 1350 } 1351 1352 case sandbox.config.HypervisorConfig.BlockDeviceDriver == config.VirtioSCSI: 1353 rootfs.Driver = kataSCSIDevType 1354 rootfs.Source = blockDrive.SCSIAddr 1355 default: 1356 return nil, fmt.Errorf("Unknown block device driver: %s", sandbox.config.HypervisorConfig.BlockDeviceDriver) 1357 } 1358 1359 rootfs.MountPoint = rootPathParent 1360 rootfs.Fstype = c.state.Fstype 1361 1362 if c.state.Fstype == "xfs" { 1363 rootfs.Options = []string{"nouuid"} 1364 } 1365 1366 // Ensure container mount destination exists 1367 // TODO: remove dependency on shared fs path. shared fs is just one kind of storage source. 1368 // we should not always use shared fs path for all kinds of storage. Instead, all storage 1369 // should be bind mounted to a tmpfs path for containers to use. 1370 if err := os.MkdirAll(filepath.Join(getMountPath(c.sandbox.id), c.id, c.rootfsSuffix), DirMode); err != nil { 1371 return nil, err 1372 } 1373 return rootfs, nil 1374 } 1375 1376 // This is not a block based device rootfs. We are going to bind mount it into the shared drive 1377 // between the host and the guest. 1378 // With virtiofs/9pfs we don't need to ask the agent to mount the rootfs as the shared directory 1379 // (kataGuestSharedDir) is already mounted in the guest. We only need to mount the rootfs from 1380 // the host and it will show up in the guest. 1381 if err := bindMountContainerRootfs(k.ctx, getMountPath(sandbox.id), c.id, c.rootFs.Target, false); err != nil { 1382 return nil, err 1383 } 1384 1385 return nil, nil 1386 } 1387 1388 func (k *kataAgent) hasAgentDebugConsole(sandbox *Sandbox) bool { 1389 for _, p := range sandbox.config.HypervisorConfig.KernelParams { 1390 if p.Key == "agent.debug_console" { 1391 k.Logger().Info("agent has debug console") 1392 return true 1393 } 1394 } 1395 return false 1396 } 1397 1398 func (k *kataAgent) createContainer(sandbox *Sandbox, c *Container) (p *Process, err error) { 1399 span, _ := k.trace("createContainer") 1400 defer span.Finish() 1401 1402 var ctrStorages []*grpc.Storage 1403 var ctrDevices []*grpc.Device 1404 var rootfs *grpc.Storage 1405 1406 // This is the guest absolute root path for that container. 1407 rootPathParent := filepath.Join(kataGuestSharedDir(), c.id) 1408 rootPath := filepath.Join(rootPathParent, c.rootfsSuffix) 1409 1410 // In case the container creation fails, the following defer statement 1411 // takes care of rolling back actions previously performed. 1412 defer func() { 1413 if err != nil { 1414 k.Logger().WithError(err).Error("createContainer failed") 1415 k.rollbackFailingContainerCreation(c) 1416 } 1417 }() 1418 1419 // setup rootfs -- if its block based, we'll receive a non-nil storage object representing 1420 // the block device for the rootfs, which us utilized for mounting in the guest. This'll be handled 1421 // already for non-block based rootfs 1422 if rootfs, err = k.buildContainerRootfs(sandbox, c, rootPathParent); err != nil { 1423 return nil, err 1424 } 1425 1426 if rootfs != nil { 1427 // Add rootfs to the list of container storage. 1428 // We only need to do this for block based rootfs, as we 1429 // want the agent to mount it into the right location 1430 // (kataGuestSharedDir/ctrID/ 1431 ctrStorages = append(ctrStorages, rootfs) 1432 } 1433 1434 ociSpec := c.GetPatchedOCISpec() 1435 if ociSpec == nil { 1436 return nil, errorMissingOCISpec 1437 } 1438 1439 // Handle container mounts 1440 newMounts, ignoredMounts, err := c.mountSharedDirMounts(getSharePath(sandbox.id), getMountPath(sandbox.id), kataGuestSharedDir()) 1441 if err != nil { 1442 return nil, err 1443 } 1444 1445 k.handleShm(ociSpec.Mounts, sandbox) 1446 1447 epheStorages := k.handleEphemeralStorage(ociSpec.Mounts) 1448 ctrStorages = append(ctrStorages, epheStorages...) 1449 1450 k.Logger().WithField("ociSpec Hugepage Resources", ociSpec.Linux.Resources.HugepageLimits).Debug("ociSpec HugepageLimit") 1451 hugepages, err := k.handleHugepages(ociSpec.Mounts, ociSpec.Linux.Resources.HugepageLimits) 1452 if err != nil { 1453 return nil, err 1454 } 1455 ctrStorages = append(ctrStorages, hugepages...) 1456 1457 localStorages := k.handleLocalStorage(ociSpec.Mounts, sandbox.id, c.rootfsSuffix) 1458 ctrStorages = append(ctrStorages, localStorages...) 1459 1460 // We replace all OCI mount sources that match our container mount 1461 // with the right source path (The guest one). 1462 if err = k.replaceOCIMountSource(ociSpec, newMounts); err != nil { 1463 return nil, err 1464 } 1465 1466 // Remove all mounts that should be ignored from the spec 1467 if err = k.removeIgnoredOCIMount(ociSpec, ignoredMounts); err != nil { 1468 return nil, err 1469 } 1470 1471 // Append container devices for block devices passed with --device. 1472 ctrDevices = k.appendDevices(ctrDevices, c) 1473 1474 // Handle all the volumes that are block device files. 1475 // Note this call modifies the list of container devices to make sure 1476 // all hotplugged devices are unplugged, so this needs be done 1477 // after devices passed with --device are handled. 1478 volumeStorages, err := k.handleBlockVolumes(c) 1479 if err != nil { 1480 return nil, err 1481 } 1482 1483 if err := k.replaceOCIMountsForStorages(ociSpec, volumeStorages); err != nil { 1484 return nil, err 1485 } 1486 1487 ctrStorages = append(ctrStorages, volumeStorages...) 1488 1489 grpcSpec, err := grpc.OCItoGRPC(ociSpec) 1490 if err != nil { 1491 return nil, err 1492 } 1493 1494 // We need to give the OCI spec our absolute rootfs path in the guest. 1495 grpcSpec.Root.Path = rootPath 1496 1497 sharedPidNs := k.handlePidNamespace(grpcSpec, sandbox) 1498 1499 agentPidNs := k.checkAgentPidNs(c) 1500 if agentPidNs { 1501 if !sandbox.config.EnableAgentPidNs { 1502 agentPidNs = false 1503 k.Logger().Warn("Env variable for sharing container pid namespace with the agent set, but the runtime configuration does not allow this") 1504 } else { 1505 k.Logger().Warn("Container will share PID namespace with the agent") 1506 } 1507 } 1508 passSeccomp := !sandbox.config.DisableGuestSeccomp && sandbox.seccompSupported 1509 1510 // We need to constraint the spec to make sure we're not passing 1511 // irrelevant information to the agent. 1512 k.constraintGRPCSpec(grpcSpec, passSeccomp) 1513 1514 req := &grpc.CreateContainerRequest{ 1515 ContainerId: c.id, 1516 ExecId: c.id, 1517 Storages: ctrStorages, 1518 Devices: ctrDevices, 1519 OCI: grpcSpec, 1520 SandboxPidns: sharedPidNs, 1521 AgentPidns: agentPidNs, 1522 } 1523 1524 if _, err = k.sendReq(req); err != nil { 1525 return nil, err 1526 } 1527 1528 createNSList := []ns.NSType{ns.NSTypePID} 1529 1530 enterNSList := []ns.Namespace{} 1531 if sandbox.networkNS.NetNsPath != "" { 1532 enterNSList = append(enterNSList, ns.Namespace{ 1533 Path: sandbox.networkNS.NetNsPath, 1534 Type: ns.NSTypeNet, 1535 }) 1536 } 1537 1538 // Ask to the shim to print the agent logs, if it's the process who monitors the sandbox and use_vsock is true (no proxy) 1539 // Don't read the console socket if agent debug console is enabled. 1540 var consoleURL string 1541 if sandbox.config.HypervisorConfig.UseVSock && 1542 c.GetAnnotations()[vcAnnotations.ContainerTypeKey] == string(PodSandbox) && 1543 !k.hasAgentDebugConsole(sandbox) { 1544 consoleURL, err = sandbox.hypervisor.getSandboxConsole(sandbox.id) 1545 if err != nil { 1546 return nil, err 1547 } 1548 } 1549 1550 return prepareAndStartShim(sandbox, k.shim, c.id, req.ExecId, 1551 k.state.URL, consoleURL, c.config.Cmd, createNSList, enterNSList) 1552 } 1553 1554 // handleEphemeralStorage handles ephemeral storages by 1555 // creating a Storage from corresponding source of the mount point 1556 func (k *kataAgent) handleEphemeralStorage(mounts []specs.Mount) []*grpc.Storage { 1557 var epheStorages []*grpc.Storage 1558 for idx, mnt := range mounts { 1559 if mnt.Type == KataEphemeralDevType { 1560 // Set the mount source path to a path that resides inside the VM 1561 mounts[idx].Source = filepath.Join(ephemeralPath(), filepath.Base(mnt.Source)) 1562 // Set the mount type to "bind" 1563 mounts[idx].Type = "bind" 1564 1565 // Create a storage struct so that kata agent is able to create 1566 // tmpfs backed volume inside the VM 1567 epheStorage := &grpc.Storage{ 1568 Driver: KataEphemeralDevType, 1569 Source: "tmpfs", 1570 Fstype: "tmpfs", 1571 MountPoint: mounts[idx].Source, 1572 } 1573 epheStorages = append(epheStorages, epheStorage) 1574 } 1575 } 1576 return epheStorages 1577 } 1578 1579 // handleHugePages handles hugepages storage by 1580 // creating a Storage from corresponding source of the mount point 1581 func (k *kataAgent) handleHugepages(mounts []specs.Mount, hugepageLimits []specs.LinuxHugepageLimit) ([]*grpc.Storage, error) { 1582 //Map to hold the total memory of each type of hugepages 1583 optionsMap := make(map[int64]string) 1584 1585 for _, hp := range hugepageLimits { 1586 if hp.Limit != 0 { 1587 k.Logger().WithFields(logrus.Fields{ 1588 "Pagesize": hp.Pagesize, 1589 "Limit": hp.Limit, 1590 }).Info("hugepage request") 1591 //example Pagesize 2MB, 1GB etc. The Limit are in Bytes 1592 pageSize, err := units.RAMInBytes(hp.Pagesize) 1593 if err != nil { 1594 k.Logger().Error("Unable to convert pagesize to bytes") 1595 return nil, err 1596 } 1597 totalHpSizeStr := strconv.FormatUint(hp.Limit, 10) 1598 optionsMap[pageSize] = totalHpSizeStr 1599 } 1600 } 1601 1602 var hugepages []*grpc.Storage 1603 for idx, mnt := range mounts { 1604 if mnt.Type != KataLocalDevType { 1605 continue 1606 } 1607 //HugePages mount Type is Local 1608 if _, fsType, fsOptions, _ := utils.GetDevicePathAndFsTypeOptions(mnt.Source); fsType == "hugetlbfs" { 1609 k.Logger().WithField("fsOptions", fsOptions).Debug("hugepage mount options") 1610 //Find the pagesize from the mountpoint options 1611 pagesizeOpt := getPagesizeFromOpt(fsOptions) 1612 if pagesizeOpt == "" { 1613 return nil, fmt.Errorf("No pagesize option found in filesystem mount options") 1614 } 1615 pageSize, err := units.RAMInBytes(pagesizeOpt) 1616 if err != nil { 1617 k.Logger().Error("Unable to convert pagesize from fs mount options to bytes") 1618 return nil, err 1619 } 1620 //Create mount option string 1621 options := fmt.Sprintf("pagesize=%s,size=%s", strconv.FormatInt(pageSize, 10), optionsMap[pageSize]) 1622 k.Logger().WithField("Hugepage options string", options).Debug("hugepage mount options") 1623 // Set the mount source path to a path that resides inside the VM 1624 mounts[idx].Source = filepath.Join(ephemeralPath(), filepath.Base(mnt.Source)) 1625 // Set the mount type to "bind" 1626 mounts[idx].Type = "bind" 1627 1628 // Create a storage struct so that kata agent is able to create 1629 // hugetlbfs backed volume inside the VM 1630 hugepage := &grpc.Storage{ 1631 Driver: KataEphemeralDevType, 1632 Source: "nodev", 1633 Fstype: "hugetlbfs", 1634 MountPoint: mounts[idx].Source, 1635 Options: []string{options}, 1636 } 1637 hugepages = append(hugepages, hugepage) 1638 } 1639 1640 } 1641 return hugepages, nil 1642 } 1643 1644 // handleLocalStorage handles local storage within the VM 1645 // by creating a directory in the VM from the source of the mount point. 1646 func (k *kataAgent) handleLocalStorage(mounts []specs.Mount, sandboxID string, rootfsSuffix string) []*grpc.Storage { 1647 var localStorages []*grpc.Storage 1648 for idx, mnt := range mounts { 1649 if mnt.Type == KataLocalDevType { 1650 // Set the mount source path to a the desired directory point in the VM. 1651 // In this case it is located in the sandbox directory. 1652 // We rely on the fact that the first container in the VM has the same ID as the sandbox ID. 1653 // In Kubernetes, this is usually the pause container and we depend on it existing for 1654 // local directories to work. 1655 mounts[idx].Source = filepath.Join(kataGuestSharedDir(), sandboxID, rootfsSuffix, KataLocalDevType, filepath.Base(mnt.Source)) 1656 1657 // Create a storage struct so that the kata agent is able to create the 1658 // directory inside the VM. 1659 localStorage := &grpc.Storage{ 1660 Driver: KataLocalDevType, 1661 Source: KataLocalDevType, 1662 Fstype: KataLocalDevType, 1663 MountPoint: mounts[idx].Source, 1664 Options: localDirOptions, 1665 } 1666 localStorages = append(localStorages, localStorage) 1667 } 1668 } 1669 return localStorages 1670 } 1671 1672 // handleDeviceBlockVolume handles volume that is block device file 1673 // and DeviceBlock type. 1674 func (k *kataAgent) handleDeviceBlockVolume(c *Container, m Mount, device api.Device) (*grpc.Storage, error) { 1675 vol := &grpc.Storage{} 1676 1677 blockDrive, ok := device.GetDeviceInfo().(*config.BlockDrive) 1678 if !ok || blockDrive == nil { 1679 k.Logger().Error("malformed block drive") 1680 return nil, fmt.Errorf("malformed block drive") 1681 } 1682 switch { 1683 // pmem volumes case 1684 case blockDrive.Pmem: 1685 vol.Driver = kataNvdimmDevType 1686 vol.Source = fmt.Sprintf("/dev/pmem%s", blockDrive.NvdimmID) 1687 vol.Fstype = blockDrive.Format 1688 vol.Options = []string{"dax"} 1689 case c.sandbox.config.HypervisorConfig.BlockDeviceDriver == config.VirtioBlockCCW: 1690 vol.Driver = kataBlkCCWDevType 1691 vol.Source = blockDrive.DevNo 1692 case c.sandbox.config.HypervisorConfig.BlockDeviceDriver == config.VirtioBlock: 1693 vol.Driver = kataBlkDevType 1694 if blockDrive.PCIPath.IsNil() { 1695 vol.Source = blockDrive.VirtPath 1696 } else { 1697 vol.Source = blockDrive.PCIPath.String() 1698 } 1699 case c.sandbox.config.HypervisorConfig.BlockDeviceDriver == config.VirtioMmio: 1700 vol.Driver = kataMmioBlkDevType 1701 vol.Source = blockDrive.VirtPath 1702 case c.sandbox.config.HypervisorConfig.BlockDeviceDriver == config.VirtioSCSI: 1703 vol.Driver = kataSCSIDevType 1704 vol.Source = blockDrive.SCSIAddr 1705 default: 1706 return nil, fmt.Errorf("Unknown block device driver: %s", c.sandbox.config.HypervisorConfig.BlockDeviceDriver) 1707 } 1708 1709 vol.MountPoint = m.Destination 1710 1711 // If no explicit FS Type or Options are being set, then let's use what is provided for the particular mount: 1712 if vol.Fstype == "" { 1713 vol.Fstype = m.Type 1714 } 1715 if len(vol.Options) == 0 { 1716 vol.Options = m.Options 1717 } 1718 1719 return vol, nil 1720 } 1721 1722 // handleVhostUserBlkVolume handles volume that is block device file 1723 // and VhostUserBlk type. 1724 func (k *kataAgent) handleVhostUserBlkVolume(c *Container, m Mount, device api.Device) (*grpc.Storage, error) { 1725 vol := &grpc.Storage{} 1726 1727 d, ok := device.GetDeviceInfo().(*config.VhostUserDeviceAttrs) 1728 if !ok || d == nil { 1729 k.Logger().Error("malformed vhost-user blk drive") 1730 return nil, fmt.Errorf("malformed vhost-user blk drive") 1731 } 1732 1733 vol.Driver = kataBlkDevType 1734 vol.Source = d.PCIPath.String() 1735 vol.Fstype = "bind" 1736 vol.Options = []string{"bind"} 1737 vol.MountPoint = m.Destination 1738 1739 return vol, nil 1740 } 1741 1742 // handleBlockVolumes handles volumes that are block devices files 1743 // by passing the block devices as Storage to the agent. 1744 func (k *kataAgent) handleBlockVolumes(c *Container) ([]*grpc.Storage, error) { 1745 1746 var volumeStorages []*grpc.Storage 1747 1748 for _, m := range c.mounts { 1749 id := m.BlockDeviceID 1750 1751 if len(id) == 0 { 1752 continue 1753 } 1754 1755 // Add the block device to the list of container devices, to make sure the 1756 // device is detached with detachDevices() for a container. 1757 c.devices = append(c.devices, ContainerDevice{ID: id, ContainerPath: m.Destination}) 1758 1759 var vol *grpc.Storage 1760 1761 device := c.sandbox.devManager.GetDeviceByID(id) 1762 if device == nil { 1763 k.Logger().WithField("device", id).Error("failed to find device by id") 1764 return nil, fmt.Errorf("Failed to find device by id (id=%s)", id) 1765 } 1766 1767 var err error 1768 switch device.DeviceType() { 1769 case config.DeviceBlock: 1770 vol, err = k.handleDeviceBlockVolume(c, m, device) 1771 case config.VhostUserBlk: 1772 vol, err = k.handleVhostUserBlkVolume(c, m, device) 1773 default: 1774 k.Logger().Error("Unknown device type") 1775 continue 1776 } 1777 1778 if vol == nil || err != nil { 1779 return nil, err 1780 } 1781 1782 volumeStorages = append(volumeStorages, vol) 1783 } 1784 1785 return volumeStorages, nil 1786 } 1787 1788 // handlePidNamespace checks if Pid namespace for a container needs to be shared with its sandbox 1789 // pid namespace. This function also modifies the grpc spec to remove the pid namespace 1790 // from the list of namespaces passed to the agent. 1791 func (k *kataAgent) handlePidNamespace(grpcSpec *grpc.Spec, sandbox *Sandbox) bool { 1792 sharedPidNs := false 1793 pidIndex := -1 1794 1795 for i, ns := range grpcSpec.Linux.Namespaces { 1796 if ns.Type != string(specs.PIDNamespace) { 1797 continue 1798 } 1799 1800 pidIndex = i 1801 // host pidns path does not make sense in kata. Let's just align it with 1802 // sandbox namespace whenever it is set. 1803 if ns.Path != "" { 1804 sharedPidNs = true 1805 } 1806 break 1807 } 1808 1809 // Remove pid namespace. 1810 if pidIndex >= 0 { 1811 grpcSpec.Linux.Namespaces = append(grpcSpec.Linux.Namespaces[:pidIndex], grpcSpec.Linux.Namespaces[pidIndex+1:]...) 1812 } 1813 1814 return sharedPidNs 1815 } 1816 1817 // checkAgentPidNs checks if environment variable KATA_AGENT_PIDNS has been set for a containers 1818 // This variable is used to indicate if the containers pid namespace should be shared 1819 // with the agent pidns. This approach was taken due to the lack of support for container level annotations. 1820 func (k *kataAgent) checkAgentPidNs(container *Container) bool { 1821 agentPidNs := false 1822 1823 for _, env := range container.config.Cmd.Envs { 1824 if env.Var == agentPidEnv { 1825 if val, err := strconv.ParseBool(env.Value); err == nil { 1826 agentPidNs = val 1827 } 1828 } 1829 } 1830 1831 return agentPidNs 1832 } 1833 1834 func (k *kataAgent) startContainer(sandbox *Sandbox, c *Container) error { 1835 span, _ := k.trace("startContainer") 1836 defer span.Finish() 1837 1838 req := &grpc.StartContainerRequest{ 1839 ContainerId: c.id, 1840 } 1841 1842 _, err := k.sendReq(req) 1843 return err 1844 } 1845 1846 func (k *kataAgent) stopContainer(sandbox *Sandbox, c Container) error { 1847 span, _ := k.trace("stopContainer") 1848 defer span.Finish() 1849 1850 _, err := k.sendReq(&grpc.RemoveContainerRequest{ContainerId: c.id}) 1851 return err 1852 } 1853 1854 func (k *kataAgent) signalProcess(c *Container, processID string, signal syscall.Signal, all bool) error { 1855 execID := processID 1856 if all { 1857 // kata agent uses empty execId to signal all processes in a container 1858 execID = "" 1859 } 1860 req := &grpc.SignalProcessRequest{ 1861 ContainerId: c.id, 1862 ExecId: execID, 1863 Signal: uint32(signal), 1864 } 1865 1866 _, err := k.sendReq(req) 1867 return err 1868 } 1869 1870 func (k *kataAgent) winsizeProcess(c *Container, processID string, height, width uint32) error { 1871 req := &grpc.TtyWinResizeRequest{ 1872 ContainerId: c.id, 1873 ExecId: processID, 1874 Row: height, 1875 Column: width, 1876 } 1877 1878 _, err := k.sendReq(req) 1879 return err 1880 } 1881 1882 func (k *kataAgent) processListContainer(sandbox *Sandbox, c Container, options ProcessListOptions) (ProcessList, error) { 1883 req := &grpc.ListProcessesRequest{ 1884 ContainerId: c.id, 1885 Format: options.Format, 1886 Args: options.Args, 1887 } 1888 1889 resp, err := k.sendReq(req) 1890 if err != nil { 1891 return nil, err 1892 } 1893 1894 processList, ok := resp.(*grpc.ListProcessesResponse) 1895 if !ok { 1896 return nil, fmt.Errorf("Bad list processes response") 1897 } 1898 1899 return processList.ProcessList, nil 1900 } 1901 1902 func (k *kataAgent) updateContainer(sandbox *Sandbox, c Container, resources specs.LinuxResources) error { 1903 grpcResources, err := grpc.ResourcesOCItoGRPC(&resources) 1904 if err != nil { 1905 return err 1906 } 1907 1908 req := &grpc.UpdateContainerRequest{ 1909 ContainerId: c.id, 1910 Resources: grpcResources, 1911 } 1912 1913 _, err = k.sendReq(req) 1914 return err 1915 } 1916 1917 func (k *kataAgent) pauseContainer(sandbox *Sandbox, c Container) error { 1918 req := &grpc.PauseContainerRequest{ 1919 ContainerId: c.id, 1920 } 1921 1922 _, err := k.sendReq(req) 1923 return err 1924 } 1925 1926 func (k *kataAgent) resumeContainer(sandbox *Sandbox, c Container) error { 1927 req := &grpc.ResumeContainerRequest{ 1928 ContainerId: c.id, 1929 } 1930 1931 _, err := k.sendReq(req) 1932 return err 1933 } 1934 1935 func (k *kataAgent) memHotplugByProbe(addr uint64, sizeMB uint32, memorySectionSizeMB uint32) error { 1936 if memorySectionSizeMB == uint32(0) { 1937 return fmt.Errorf("memorySectionSizeMB couldn't be zero") 1938 } 1939 // hot-added memory device should be sliced into the size of memory section, which is the basic unit for 1940 // memory hotplug 1941 numSection := uint64(sizeMB / memorySectionSizeMB) 1942 var addrList []uint64 1943 index := uint64(0) 1944 for index < numSection { 1945 k.Logger().WithFields(logrus.Fields{ 1946 "addr": fmt.Sprintf("0x%x", addr+(index*uint64(memorySectionSizeMB))<<20), 1947 }).Debugf("notify guest kernel the address of memory device") 1948 addrList = append(addrList, addr+(index*uint64(memorySectionSizeMB))<<20) 1949 index++ 1950 } 1951 req := &grpc.MemHotplugByProbeRequest{ 1952 MemHotplugProbeAddr: addrList, 1953 } 1954 1955 _, err := k.sendReq(req) 1956 return err 1957 } 1958 1959 func (k *kataAgent) onlineCPUMem(cpus uint32, cpuOnly bool) error { 1960 req := &grpc.OnlineCPUMemRequest{ 1961 Wait: false, 1962 NbCpus: cpus, 1963 CpuOnly: cpuOnly, 1964 } 1965 1966 _, err := k.sendReq(req) 1967 return err 1968 } 1969 1970 func (k *kataAgent) statsContainer(sandbox *Sandbox, c Container) (*ContainerStats, error) { 1971 req := &grpc.StatsContainerRequest{ 1972 ContainerId: c.id, 1973 } 1974 1975 returnStats, err := k.sendReq(req) 1976 1977 if err != nil { 1978 return nil, err 1979 } 1980 1981 stats, ok := returnStats.(*grpc.StatsContainerResponse) 1982 if !ok { 1983 return nil, fmt.Errorf("irregular response container stats") 1984 } 1985 1986 data, err := json.Marshal(stats.CgroupStats) 1987 if err != nil { 1988 return nil, err 1989 } 1990 1991 var cgroupStats CgroupStats 1992 err = json.Unmarshal(data, &cgroupStats) 1993 if err != nil { 1994 return nil, err 1995 } 1996 containerStats := &ContainerStats{ 1997 CgroupStats: &cgroupStats, 1998 } 1999 return containerStats, nil 2000 } 2001 2002 func (k *kataAgent) connect() error { 2003 if k.dead { 2004 return errors.New("Dead agent") 2005 } 2006 // lockless quick pass 2007 if k.client != nil { 2008 return nil 2009 } 2010 2011 span, _ := k.trace("connect") 2012 defer span.Finish() 2013 2014 // This is for the first connection only, to prevent race 2015 k.Lock() 2016 defer k.Unlock() 2017 if k.client != nil { 2018 return nil 2019 } 2020 2021 if k.state.ProxyPid > 0 { 2022 // check that proxy is running before talk with it avoiding long timeouts 2023 if err := syscall.Kill(k.state.ProxyPid, syscall.Signal(0)); err != nil { 2024 return errors.New("Proxy is not running") 2025 } 2026 } 2027 2028 k.Logger().WithField("url", k.state.URL).WithField("proxy", k.state.ProxyPid).Info("New client") 2029 client, err := kataclient.NewAgentClient(k.ctx, k.state.URL, k.proxyBuiltIn) 2030 if err != nil { 2031 k.dead = true 2032 return err 2033 } 2034 2035 k.installReqFunc(client) 2036 k.client = client 2037 2038 return nil 2039 } 2040 2041 func (k *kataAgent) disconnect() error { 2042 span, _ := k.trace("disconnect") 2043 defer span.Finish() 2044 2045 k.Lock() 2046 defer k.Unlock() 2047 2048 if k.client == nil { 2049 return nil 2050 } 2051 2052 if err := k.client.Close(); err != nil && grpcStatus.Convert(err).Code() != codes.Canceled { 2053 return err 2054 } 2055 2056 k.client = nil 2057 k.reqHandlers = nil 2058 2059 return nil 2060 } 2061 2062 // check grpc server is serving 2063 func (k *kataAgent) check() error { 2064 span, _ := k.trace("check") 2065 defer span.Finish() 2066 2067 _, err := k.sendReq(&grpc.CheckRequest{}) 2068 if err != nil { 2069 err = fmt.Errorf("Failed to check if grpc server is working: %s", err) 2070 } 2071 return err 2072 } 2073 2074 func (k *kataAgent) waitProcess(c *Container, processID string) (int32, error) { 2075 span, _ := k.trace("waitProcess") 2076 defer span.Finish() 2077 2078 resp, err := k.sendReq(&grpc.WaitProcessRequest{ 2079 ContainerId: c.id, 2080 ExecId: processID, 2081 }) 2082 if err != nil { 2083 return 0, err 2084 } 2085 2086 return resp.(*grpc.WaitProcessResponse).Status, nil 2087 } 2088 2089 func (k *kataAgent) writeProcessStdin(c *Container, ProcessID string, data []byte) (int, error) { 2090 resp, err := k.sendReq(&grpc.WriteStreamRequest{ 2091 ContainerId: c.id, 2092 ExecId: ProcessID, 2093 Data: data, 2094 }) 2095 2096 if err != nil { 2097 return 0, err 2098 } 2099 2100 return int(resp.(*grpc.WriteStreamResponse).Len), nil 2101 } 2102 2103 func (k *kataAgent) closeProcessStdin(c *Container, ProcessID string) error { 2104 _, err := k.sendReq(&grpc.CloseStdinRequest{ 2105 ContainerId: c.id, 2106 ExecId: ProcessID, 2107 }) 2108 2109 return err 2110 } 2111 2112 func (k *kataAgent) reseedRNG(data []byte) error { 2113 _, err := k.sendReq(&grpc.ReseedRandomDevRequest{ 2114 Data: data, 2115 }) 2116 2117 return err 2118 } 2119 2120 type reqFunc func(context.Context, interface{}, ...golangGrpc.CallOption) (interface{}, error) 2121 2122 func (k *kataAgent) installReqFunc(c *kataclient.AgentClient) { 2123 k.reqHandlers = make(map[string]reqFunc) 2124 k.reqHandlers[grpcCheckRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2125 return k.client.Check(ctx, req.(*grpc.CheckRequest), opts...) 2126 } 2127 k.reqHandlers[grpcExecProcessRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2128 return k.client.ExecProcess(ctx, req.(*grpc.ExecProcessRequest), opts...) 2129 } 2130 k.reqHandlers[grpcCreateSandboxRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2131 return k.client.CreateSandbox(ctx, req.(*grpc.CreateSandboxRequest), opts...) 2132 } 2133 k.reqHandlers[grpcDestroySandboxRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2134 return k.client.DestroySandbox(ctx, req.(*grpc.DestroySandboxRequest), opts...) 2135 } 2136 k.reqHandlers[grpcCreateContainerRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2137 return k.client.CreateContainer(ctx, req.(*grpc.CreateContainerRequest), opts...) 2138 } 2139 k.reqHandlers[grpcStartContainerRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2140 return k.client.StartContainer(ctx, req.(*grpc.StartContainerRequest), opts...) 2141 } 2142 k.reqHandlers[grpcRemoveContainerRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2143 return k.client.RemoveContainer(ctx, req.(*grpc.RemoveContainerRequest), opts...) 2144 } 2145 k.reqHandlers[grpcSignalProcessRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2146 return k.client.SignalProcess(ctx, req.(*grpc.SignalProcessRequest), opts...) 2147 } 2148 k.reqHandlers[grpcUpdateRoutesRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2149 return k.client.UpdateRoutes(ctx, req.(*grpc.UpdateRoutesRequest), opts...) 2150 } 2151 k.reqHandlers[grpcUpdateInterfaceRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2152 return k.client.UpdateInterface(ctx, req.(*grpc.UpdateInterfaceRequest), opts...) 2153 } 2154 k.reqHandlers[grpcListInterfacesRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2155 return k.client.ListInterfaces(ctx, req.(*grpc.ListInterfacesRequest), opts...) 2156 } 2157 k.reqHandlers[grpcListRoutesRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2158 return k.client.ListRoutes(ctx, req.(*grpc.ListRoutesRequest), opts...) 2159 } 2160 k.reqHandlers[grpcAddARPNeighborsRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2161 return k.client.AddARPNeighbors(ctx, req.(*grpc.AddARPNeighborsRequest), opts...) 2162 } 2163 k.reqHandlers[grpcOnlineCPUMemRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2164 return k.client.OnlineCPUMem(ctx, req.(*grpc.OnlineCPUMemRequest), opts...) 2165 } 2166 k.reqHandlers[grpcListProcessesRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2167 return k.client.ListProcesses(ctx, req.(*grpc.ListProcessesRequest), opts...) 2168 } 2169 k.reqHandlers[grpcUpdateContainerRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2170 return k.client.UpdateContainer(ctx, req.(*grpc.UpdateContainerRequest), opts...) 2171 } 2172 k.reqHandlers[grpcWaitProcessRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2173 return k.client.WaitProcess(ctx, req.(*grpc.WaitProcessRequest), opts...) 2174 } 2175 k.reqHandlers[grpcTtyWinResizeRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2176 return k.client.TtyWinResize(ctx, req.(*grpc.TtyWinResizeRequest), opts...) 2177 } 2178 k.reqHandlers[grpcWriteStreamRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2179 return k.client.WriteStdin(ctx, req.(*grpc.WriteStreamRequest), opts...) 2180 } 2181 k.reqHandlers[grpcCloseStdinRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2182 return k.client.CloseStdin(ctx, req.(*grpc.CloseStdinRequest), opts...) 2183 } 2184 k.reqHandlers[grpcStatsContainerRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2185 return k.client.StatsContainer(ctx, req.(*grpc.StatsContainerRequest), opts...) 2186 } 2187 k.reqHandlers[grpcPauseContainerRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2188 return k.client.PauseContainer(ctx, req.(*grpc.PauseContainerRequest), opts...) 2189 } 2190 k.reqHandlers[grpcResumeContainerRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2191 return k.client.ResumeContainer(ctx, req.(*grpc.ResumeContainerRequest), opts...) 2192 } 2193 k.reqHandlers[grpcReseedRandomDevRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2194 return k.client.ReseedRandomDev(ctx, req.(*grpc.ReseedRandomDevRequest), opts...) 2195 } 2196 k.reqHandlers[grpcGuestDetailsRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2197 return k.client.GetGuestDetails(ctx, req.(*grpc.GuestDetailsRequest), opts...) 2198 } 2199 k.reqHandlers[grpcMemHotplugByProbeRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2200 return k.client.MemHotplugByProbe(ctx, req.(*grpc.MemHotplugByProbeRequest), opts...) 2201 } 2202 k.reqHandlers[grpcCopyFileRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2203 return k.client.CopyFile(ctx, req.(*grpc.CopyFileRequest), opts...) 2204 } 2205 k.reqHandlers[grpcSetGuestDateTimeRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2206 return k.client.SetGuestDateTime(ctx, req.(*grpc.SetGuestDateTimeRequest), opts...) 2207 } 2208 k.reqHandlers[grpcStartTracingRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2209 return k.client.StartTracing(ctx, req.(*grpc.StartTracingRequest), opts...) 2210 } 2211 k.reqHandlers[grpcStopTracingRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2212 return k.client.StopTracing(ctx, req.(*grpc.StopTracingRequest), opts...) 2213 } 2214 k.reqHandlers[grpcGetOOMEventRequest] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { 2215 return k.client.GetOOMEvent(ctx, req.(*grpc.GetOOMEventRequest), opts...) 2216 } 2217 } 2218 2219 func (k *kataAgent) getReqContext(reqName string) (ctx context.Context, cancel context.CancelFunc) { 2220 ctx = context.Background() 2221 switch reqName { 2222 case grpcWaitProcessRequest, grpcGetOOMEventRequest: 2223 // Wait and GetOOMEvent have no timeout 2224 case grpcCheckRequest: 2225 ctx, cancel = context.WithTimeout(ctx, checkRequestTimeout) 2226 default: 2227 ctx, cancel = context.WithTimeout(ctx, defaultRequestTimeout) 2228 } 2229 2230 return ctx, cancel 2231 } 2232 2233 func (k *kataAgent) sendReq(request interface{}) (interface{}, error) { 2234 span, _ := k.trace("sendReq") 2235 span.SetTag("request", request) 2236 defer span.Finish() 2237 2238 if err := k.connect(); err != nil { 2239 return nil, err 2240 } 2241 if !k.keepConn { 2242 defer k.disconnect() 2243 } 2244 2245 msgName := proto.MessageName(request.(proto.Message)) 2246 handler := k.reqHandlers[msgName] 2247 if msgName == "" || handler == nil { 2248 return nil, errors.New("Invalid request type") 2249 } 2250 message := request.(proto.Message) 2251 ctx, cancel := k.getReqContext(msgName) 2252 if cancel != nil { 2253 defer cancel() 2254 } 2255 k.Logger().WithField("name", msgName).WithField("req", message.String()).Debug("sending request") 2256 2257 return handler(ctx, request) 2258 } 2259 2260 // readStdout and readStderr are special that we cannot differentiate them with the request types... 2261 func (k *kataAgent) readProcessStdout(c *Container, processID string, data []byte) (int, error) { 2262 if err := k.connect(); err != nil { 2263 return 0, err 2264 } 2265 if !k.keepConn { 2266 defer k.disconnect() 2267 } 2268 2269 return k.readProcessStream(c.id, processID, data, k.client.ReadStdout) 2270 } 2271 2272 // readStdout and readStderr are special that we cannot differentiate them with the request types... 2273 func (k *kataAgent) readProcessStderr(c *Container, processID string, data []byte) (int, error) { 2274 if err := k.connect(); err != nil { 2275 return 0, err 2276 } 2277 if !k.keepConn { 2278 defer k.disconnect() 2279 } 2280 2281 return k.readProcessStream(c.id, processID, data, k.client.ReadStderr) 2282 } 2283 2284 type readFn func(context.Context, *grpc.ReadStreamRequest, ...golangGrpc.CallOption) (*grpc.ReadStreamResponse, error) 2285 2286 func (k *kataAgent) readProcessStream(containerID, processID string, data []byte, read readFn) (int, error) { 2287 resp, err := read(k.ctx, &grpc.ReadStreamRequest{ 2288 ContainerId: containerID, 2289 ExecId: processID, 2290 Len: uint32(len(data))}) 2291 if err == nil { 2292 copy(data, resp.Data) 2293 return len(resp.Data), nil 2294 } 2295 2296 return 0, err 2297 } 2298 2299 func (k *kataAgent) getGuestDetails(req *grpc.GuestDetailsRequest) (*grpc.GuestDetailsResponse, error) { 2300 resp, err := k.sendReq(req) 2301 if err != nil { 2302 return nil, err 2303 } 2304 2305 return resp.(*grpc.GuestDetailsResponse), nil 2306 } 2307 2308 func (k *kataAgent) setGuestDateTime(tv time.Time) error { 2309 _, err := k.sendReq(&grpc.SetGuestDateTimeRequest{ 2310 Sec: tv.Unix(), 2311 Usec: int64(tv.Nanosecond() / 1e3), 2312 }) 2313 2314 return err 2315 } 2316 2317 func (k *kataAgent) convertToKataAgentIPFamily(ipFamily int) aTypes.IPFamily { 2318 switch ipFamily { 2319 case netlink.FAMILY_V4: 2320 return aTypes.IPFamily_v4 2321 case netlink.FAMILY_V6: 2322 return aTypes.IPFamily_v6 2323 } 2324 2325 return aTypes.IPFamily_v4 2326 } 2327 2328 func (k *kataAgent) convertToIPFamily(ipFamily aTypes.IPFamily) int { 2329 switch ipFamily { 2330 case aTypes.IPFamily_v4: 2331 return netlink.FAMILY_V4 2332 case aTypes.IPFamily_v6: 2333 return netlink.FAMILY_V6 2334 } 2335 2336 return netlink.FAMILY_V4 2337 } 2338 2339 func (k *kataAgent) convertToKataAgentIPAddress(ipAddr *vcTypes.IPAddress) (aIPAddr *aTypes.IPAddress) { 2340 if ipAddr == nil { 2341 return nil 2342 } 2343 2344 aIPAddr = &aTypes.IPAddress{ 2345 Family: k.convertToKataAgentIPFamily(ipAddr.Family), 2346 Address: ipAddr.Address, 2347 Mask: ipAddr.Mask, 2348 } 2349 2350 return aIPAddr 2351 } 2352 2353 func (k *kataAgent) convertToKataAgentIPAddresses(ipAddrs []*vcTypes.IPAddress) (aIPAddrs []*aTypes.IPAddress) { 2354 for _, ipAddr := range ipAddrs { 2355 if ipAddr == nil { 2356 continue 2357 } 2358 2359 aIPAddr := k.convertToKataAgentIPAddress(ipAddr) 2360 aIPAddrs = append(aIPAddrs, aIPAddr) 2361 } 2362 2363 return aIPAddrs 2364 } 2365 2366 func (k *kataAgent) convertToIPAddresses(aIPAddrs []*aTypes.IPAddress) (ipAddrs []*vcTypes.IPAddress) { 2367 for _, aIPAddr := range aIPAddrs { 2368 if aIPAddr == nil { 2369 continue 2370 } 2371 2372 ipAddr := &vcTypes.IPAddress{ 2373 Family: k.convertToIPFamily(aIPAddr.Family), 2374 Address: aIPAddr.Address, 2375 Mask: aIPAddr.Mask, 2376 } 2377 2378 ipAddrs = append(ipAddrs, ipAddr) 2379 } 2380 2381 return ipAddrs 2382 } 2383 2384 func (k *kataAgent) convertToKataAgentInterface(iface *vcTypes.Interface) *aTypes.Interface { 2385 if iface == nil { 2386 return nil 2387 } 2388 2389 return &aTypes.Interface{ 2390 Device: iface.Device, 2391 Name: iface.Name, 2392 IPAddresses: k.convertToKataAgentIPAddresses(iface.IPAddresses), 2393 Mtu: iface.Mtu, 2394 RawFlags: iface.RawFlags, 2395 HwAddr: iface.HwAddr, 2396 PciPath: iface.PciPath.String(), 2397 } 2398 } 2399 2400 func (k *kataAgent) convertToInterfaces(aIfaces []*aTypes.Interface) ([]*vcTypes.Interface, error) { 2401 ifaces := make([]*vcTypes.Interface, 0) 2402 for _, aIface := range aIfaces { 2403 if aIface == nil { 2404 continue 2405 } 2406 2407 pcipath, err := vcTypes.PciPathFromString(aIface.PciPath) 2408 if err != nil { 2409 return nil, err 2410 } 2411 iface := &vcTypes.Interface{ 2412 Device: aIface.Device, 2413 Name: aIface.Name, 2414 IPAddresses: k.convertToIPAddresses(aIface.IPAddresses), 2415 Mtu: aIface.Mtu, 2416 HwAddr: aIface.HwAddr, 2417 PciPath: pcipath, 2418 } 2419 2420 ifaces = append(ifaces, iface) 2421 } 2422 2423 return ifaces, nil 2424 } 2425 2426 func (k *kataAgent) convertToKataAgentRoutes(routes []*vcTypes.Route) (aRoutes []*aTypes.Route) { 2427 for _, route := range routes { 2428 if route == nil { 2429 continue 2430 } 2431 2432 aRoute := &aTypes.Route{ 2433 Dest: route.Dest, 2434 Gateway: route.Gateway, 2435 Device: route.Device, 2436 Source: route.Source, 2437 Scope: route.Scope, 2438 } 2439 2440 aRoutes = append(aRoutes, aRoute) 2441 } 2442 2443 return aRoutes 2444 } 2445 2446 func (k *kataAgent) convertToKataAgentNeighbors(neighs []*vcTypes.ARPNeighbor) (aNeighs []*aTypes.ARPNeighbor) { 2447 for _, neigh := range neighs { 2448 if neigh == nil { 2449 continue 2450 } 2451 2452 aNeigh := &aTypes.ARPNeighbor{ 2453 ToIPAddress: k.convertToKataAgentIPAddress(neigh.ToIPAddress), 2454 Device: neigh.Device, 2455 State: int32(neigh.State), 2456 Lladdr: neigh.LLAddr, 2457 } 2458 2459 aNeighs = append(aNeighs, aNeigh) 2460 } 2461 2462 return aNeighs 2463 } 2464 2465 func (k *kataAgent) convertToRoutes(aRoutes []*aTypes.Route) (routes []*vcTypes.Route) { 2466 for _, aRoute := range aRoutes { 2467 if aRoute == nil { 2468 continue 2469 } 2470 2471 route := &vcTypes.Route{ 2472 Dest: aRoute.Dest, 2473 Gateway: aRoute.Gateway, 2474 Device: aRoute.Device, 2475 Source: aRoute.Source, 2476 Scope: aRoute.Scope, 2477 } 2478 2479 routes = append(routes, route) 2480 } 2481 2482 return routes 2483 } 2484 2485 func (k *kataAgent) copyFile(src, dst string) error { 2486 var st unix.Stat_t 2487 2488 err := unix.Stat(src, &st) 2489 if err != nil { 2490 return fmt.Errorf("Could not get file %s information: %v", src, err) 2491 } 2492 2493 b, err := ioutil.ReadFile(src) 2494 if err != nil { 2495 return fmt.Errorf("Could not read file %s: %v", src, err) 2496 } 2497 2498 fileSize := int64(len(b)) 2499 2500 k.Logger().WithFields(logrus.Fields{ 2501 "source": src, 2502 "dest": dst, 2503 }).Debugf("Copying file from host to guest") 2504 2505 cpReq := &grpc.CopyFileRequest{ 2506 Path: dst, 2507 DirMode: uint32(DirMode), 2508 FileMode: st.Mode, 2509 FileSize: fileSize, 2510 Uid: int32(st.Uid), 2511 Gid: int32(st.Gid), 2512 } 2513 2514 // Handle the special case where the file is empty 2515 if fileSize == 0 { 2516 _, err = k.sendReq(cpReq) 2517 return err 2518 } 2519 2520 // Copy file by parts if it's needed 2521 remainingBytes := fileSize 2522 offset := int64(0) 2523 for remainingBytes > 0 { 2524 bytesToCopy := int64(len(b)) 2525 if bytesToCopy > grpcMaxDataSize { 2526 bytesToCopy = grpcMaxDataSize 2527 } 2528 2529 cpReq.Data = b[:bytesToCopy] 2530 cpReq.Offset = offset 2531 2532 if _, err = k.sendReq(cpReq); err != nil { 2533 return fmt.Errorf("Could not send CopyFile request: %v", err) 2534 } 2535 2536 b = b[bytesToCopy:] 2537 remainingBytes -= bytesToCopy 2538 offset += grpcMaxDataSize 2539 } 2540 2541 return nil 2542 } 2543 2544 func (k *kataAgent) markDead() { 2545 k.Logger().Infof("mark agent dead") 2546 k.dead = true 2547 k.disconnect() 2548 } 2549 2550 func (k *kataAgent) cleanup(s *Sandbox) { 2551 // Unmount shared path 2552 path := getSharePath(s.id) 2553 k.Logger().WithField("path", path).Infof("cleanup agent") 2554 if err := syscall.Unmount(path, syscall.MNT_DETACH|UmountNoFollow); err != nil { 2555 k.Logger().WithError(err).Errorf("failed to unmount vm share path %s", path) 2556 } 2557 2558 // Unmount mount path 2559 path = getMountPath(s.id) 2560 if err := bindUnmountAllRootfs(k.ctx, path, s); err != nil { 2561 k.Logger().WithError(err).Errorf("failed to unmount vm mount path %s", path) 2562 } 2563 if err := os.RemoveAll(getSandboxPath(s.id)); err != nil { 2564 k.Logger().WithError(err).Errorf("failed to cleanup vm path %s", getSandboxPath(s.id)) 2565 } 2566 } 2567 2568 func (k *kataAgent) save() persistapi.AgentState { 2569 return persistapi.AgentState{ 2570 ProxyPid: k.state.ProxyPid, 2571 URL: k.state.URL, 2572 } 2573 } 2574 2575 func (k *kataAgent) load(s persistapi.AgentState) { 2576 k.state.ProxyPid = s.ProxyPid 2577 k.state.URL = s.URL 2578 } 2579 2580 func (k *kataAgent) getOOMEvent() (string, error) { 2581 req := &grpc.GetOOMEventRequest{} 2582 result, err := k.sendReq(req) 2583 if err != nil { 2584 return "", err 2585 } 2586 if oomEvent, ok := result.(*grpc.OOMEvent); ok { 2587 return oomEvent.ContainerId, nil 2588 } 2589 return "", err 2590 }