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