Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var Command = &cli.Command{ Name: "container", Aliases: []string{"c", "ctr"}, Usage: "manage cubebox", Subcommands: []*cli.Command{ ListCommand, InfoCommand, ExecCommand, ListTapCommand, }, }
View Source
var ExecCommand = &cli.Command{ Name: "exec", Usage: "exec [OPTIONS] CONTAINER COMMAND [ARG...]", UseShortOptionHandling: true, ArgsUsage: "[flags] CONTAINER COMMAND [ARG...]", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "tty", Aliases: []string{"t"}, Usage: "(Currently -t needs to correspond to -i)", }, &cli.BoolFlag{ Name: "interactive", Aliases: []string{"i"}, Usage: "Keep STDIN open even if not attached", }, &cli.StringFlag{ Name: "workdir", Aliases: []string{"w"}, Usage: "Working directory inside the container", }, &cli.BoolFlag{ Name: "detach", Aliases: []string{"d"}, Usage: "Detached mode: run command in the background", }, }, Action: func(context *cli.Context) error { var ( args = context.Args() ) if args.Len() < 2 { return fmt.Errorf("requires at least %d arg(s), only received %d", 2, args.Len()) } newArg := []string{} if args.Len() >= 2 { if args.Get(1) == "--" { newArg = append(newArg, args.Slice()[:1]...) newArg = append(newArg, args.Slice()[2:]...) } else { newArg = args.Slice() } } cntdClient, err := containerd.New(context.String("address"), containerd.WithDefaultPlatform(platforms.Default())) if err != nil { return fmt.Errorf("init containerd connect failed.%s", err) } cntCtx := namespaces.WithNamespace(gocontext.Background(), context.String("namespace")) containerIDKey := newArg[0] containers, err := findContainer(cntCtx, containerIDKey, cntdClient) if err != nil { return err } if len(containers) == 0 { containerIDKey, err = findCubeboxContainer(context, containerIDKey) if err != nil { return err } containers, err = findContainer(cntCtx, containerIDKey, cntdClient) if err != nil { return err } if len(containers) == 0 { return fmt.Errorf("no such container %s", containerIDKey) } } flagI := context.Bool("interactive") flagT := context.Bool("tty") flagD := context.Bool("detach") if flagI { if flagD { return errors.New("currently flag -i and -d cannot be specified together (FIXME)") } } if flagT { if flagD { return errors.New("currently flag -t and -d cannot be specified together (FIXME)") } } container := containers[0] pspec, err := generateExecProcessSpec(cntCtx, context, newArg, container, cntdClient) if err != nil { return fmt.Errorf("failed to generate exec process spec: %w", err) } task, err := container.Task(cntCtx, nil) if err != nil { return fmt.Errorf("failed to get task: %w", err) } var ( ioCreator cio.Creator stdinC = newStdinCloser(os.Stdin) con console.Console ) cioOpts := []cio.Opt{cio.WithFIFODir("/data/cubelet/fifo")} if flagT { con = console.Current() defer con.Reset() if err := con.SetRaw(); err != nil { return err } cioOpts = append(cioOpts, cio.WithStreams(con, con, nil), cio.WithTerminal, cio.WithFIFODir("/data/cubelet/fifo")) } else { cioOpts = append(cioOpts, cio.WithStreams(stdinC, os.Stdout, os.Stderr)) } ioCreator = cio.NewCreator(cioOpts...) execID := "exec-" + utils.GenerateID() process, err := task.Exec(cntCtx, execID, pspec, ioCreator) if err != nil { return fmt.Errorf("failed to exec: %w", err) } stdinC.SetCloser(func() { process.CloseIO(cntCtx, containerd.WithStdinCloser) }) if !flagD { defer process.Delete(cntCtx) } statusC, err := process.Wait(cntCtx) if err != nil { return fmt.Errorf("failed to get wait channel: %w", err) } if !flagD { if flagT { if err := tasks.HandleConsoleResize(cntCtx, process, con); err != nil { logrus.WithError(err).Error("console resize") } } else { sigc := commands.ForwardAllSignals(cntCtx, process) defer commands.StopCatch(sigc) } } if err := process.Start(cntCtx); err != nil { return err } if flagD { return nil } status := <-statusC code, _, err := status.Result() if err != nil { return fmt.Errorf("failed to get exit code: %w", err) } if code != 0 { return fmt.Errorf("exec failed with exit code %d", code) } return nil }, }
View Source
var InfoCommand = &cli.Command{ Name: "info", Usage: "get info about a container", ArgsUsage: "CONTAINER", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "spec", Usage: "only display the spec", }, }, Action: func(context *cli.Context) error { id := context.Args().First() if id == "" { return fmt.Errorf("container id must be provided: %w", errdefs.ErrInvalidArgument) } cntdClient, err := containerd.New(context.String("address"), containerd.WithDefaultPlatform(platforms.Default()), ) if err != nil { return err } cntCtx := namespaces.WithNamespace(gocontext.Background(), context.String("namespace")) cntCtx, cntCancel := gocontext.WithTimeout(cntCtx, context.Duration("timeout")) defer cntCancel() filters := []string{ fmt.Sprintf("id~=^%s.*$", regexp.QuoteMeta(id)), } cntrs, err := cntdClient.Containers(cntCtx, filters...) if err != nil { return err } for _, c := range cntrs { info, err := c.Info(cntCtx, containerd.WithoutRefreshedMetadata) if err != nil { if errdefs.IsNotFound(err) { log.L.WithError(err).Error("container not found") continue } return err } if context.Bool("spec") { v, err := typeurl.UnmarshalAny(info.Spec) if err != nil { fmt.Printf("failed to unmarshal container spec with url [%s]: %s", info.Spec.GetTypeUrl(), string(info.Spec.GetValue())) return err } commands.PrintAsJSON(v) return nil } if info.Spec != nil && info.Spec.GetValue() != nil { v, err := typeurl.UnmarshalAny(info.Spec) if err != nil { return fmt.Errorf("failed to unmarshal container spec with url %s: %w", info.Spec.GetTypeUrl(), err) } commands.PrintAsJSON(struct { containers.Container Spec interface{} `json:"Spec,omitempty"` }{ Container: info, Spec: v, }) return nil } commands.PrintAsJSON(info) } return nil }, }
View Source
var ListCommand = &cli.Command{ Name: "list", Aliases: []string{"ls"}, Usage: "list containers", ArgsUsage: "[flags] [<filter>, ...]\n" + "io.kubernetes.cri.container-type [container|sandbox]\n" + "io.kubernetes.cri.sandbox-id xx", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "quiet", Aliases: []string{"q"}, Usage: "print only the container id", }, &cli.BoolFlag{ Name: "all", Aliases: []string{"a"}, Usage: "Show all containers (default shows just running)", }, &cli.IntFlag{ Name: "last", Aliases: []string{"n"}, Usage: "Show n last created containers (includes all states)", }, &cli.BoolFlag{ Name: "latest", Aliases: []string{"l"}, Usage: "Show the latest created container (includes all states)", }, &cli.BoolFlag{ Name: "no-trunc", Usage: "Don't truncate output", }, &cli.StringFlag{ Name: "sandbox", Aliases: []string{"s"}, Usage: "filter sandbox", }, }, Action: func(context *cli.Context) error { var ( args = context.Args() quiet = context.Bool("quiet") filters []string ) cntdClient, err := containerd.New(context.String("address"), containerd.WithDefaultPlatform(platforms.Default()), ) if err != nil { return err } cntCtx := namespaces.WithNamespace(gocontext.Background(), context.String("namespace")) cntCtx, cntCancel := gocontext.WithTimeout(cntCtx, context.Duration("timeout")) defer cntCancel() if args.Len() == 1 { filters = []string{ fmt.Sprintf("id~=^%s.*$", regexp.QuoteMeta(args.Get(0))), } } if args.Len() == 2 { filters = append(filters, fmt.Sprintf("labels.%q==%s", args.Get(0), args.Get(1))) } if context.IsSet("sandbox") { filters = append(filters, fmt.Sprintf("labels.\"io.kubernetes.cri.sandbox-id\"==%s", context.String("sandbox"))) } containers, err := cntdClient.Containers(cntCtx, filters...) if err != nil { return err } trunc := !context.Bool("no-trunc") all := context.Bool("all") latest := context.Bool("latest") lastN := context.Int("last") withoutSandbox := context.Bool("without-sandbox-id") if lastN == -1 && latest { lastN = 1 } if !all && lastN > 0 { all = true sort.Slice(containers, func(i, j int) bool { infoI, _ := containers[i].Info(cntCtx, containerd.WithoutRefreshedMetadata) infoJ, _ := containers[j].Info(cntCtx, containerd.WithoutRefreshedMetadata) return infoI.CreatedAt.After(infoJ.CreatedAt) }) if lastN < len(containers) { containers = containers[:lastN] } } if quiet { for _, c := range containers { fmt.Printf("%s\n", c.ID()) } return nil } w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0) title := "CONTAINER" if !withoutSandbox { title += "\tSANDBOX" } title += "\tTYPE\tIMAGE\tSTATUS\tCREATED" fmt.Fprintln(w, title) for _, c := range containers { info, err := c.Info(cntCtx, containerd.WithoutRefreshedMetadata) if err != nil { if errdefs.IsNotFound(err) { log.L.WithError(err).Error("container not found") continue } return err } imageName := info.Image if imageName == "" { imageName = "-" } sandboxID := info.SandboxID if trunc && len(sandboxID) > 12 { sandboxID = sandboxID[:12] } id := c.ID() if trunc && len(id) > 12 { id = id[:12] } cStatus := ContainerStatus(cntCtx, c) ctype := getContainerType(info.Labels) if _, err := fmt.Fprintf(w, "%s", id); err != nil { return err } if !withoutSandbox { if _, err := fmt.Fprintf(w, "\t%s", sandboxID); err != nil { return err } } if _, err := fmt.Fprintf(w, "\t%s\t%s\t%s\t%s", ctype, imageName, cStatus, info.CreatedAt.Round(time.Second).Local().String(), ); err != nil { return err } if _, err := fmt.Fprint(w, "\n"); err != nil { return err } } return w.Flush() }, }
View Source
var ListTapCommand = &cli.Command{ Name: "taps", Usage: "list all taps", Flags: commands.NetworkAgentFlags(), Action: func(clictx *cli.Context) error { ctx, cancel := gocontext.WithTimeout(gocontext.Background(), clictx.Duration("timeout")) defer cancel() endpoint, err := commands.ResolveNetworkAgentEndpoint(clictx) if err != nil { return fmt.Errorf("resolve network-agent endpoint: %w", err) } naClient, err := networkagentclient.NewClient(endpoint) if err != nil { return fmt.Errorf("create network-agent client for %q: %w", endpoint, err) } listResp, err := naClient.ListNetworks(ctx, &networkagentclient.ListNetworksRequest{}) if err != nil { return fmt.Errorf("list networks from network-agent: %w", err) } managedByIfindex := make(map[int]networkagentclient.NetworkState, len(listResp.Networks)) managedByTapName := make(map[string]networkagentclient.NetworkState, len(listResp.Networks)) for _, network := range listResp.Networks { if network.TapIfIndex != 0 { managedByIfindex[int(network.TapIfIndex)] = network } if network.TapName != "" { managedByTapName[network.TapName] = network } } links, err := netlink.LinkList() if err != nil { return fmt.Errorf("list netlink: %w", err) } w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0) fmt.Fprintln(w, "IP\tUSED\tINCUBEVS\tIFINDEX\tPORTMAPPING") var displayed []displayTap for _, link := range links { tap, ok := link.(*netlink.Tuntap) if !ok { continue } if tap.Mode != netlink.TUNTAP_MODE_TAP { continue } if !strings.HasPrefix(tap.Name, tapDeviceNamePrefix) { continue } s := tap.Name[len(tapDeviceNamePrefix):] ip := net.ParseIP(s) if ip == nil { fmt.Fprintf(os.Stderr, "invalid ip %q, skip", tap.Name) continue } managed, incubevs := managedByIfindex[tap.Index] if !incubevs { managed, incubevs = managedByTapName[tap.Name] } pm := append([]networkagentclient.PortMapping(nil), managed.PortMappings...) sort.Slice(pm, func(i, j int) bool { if pm[i].ContainerPort == pm[j].ContainerPort { return pm[i].HostPort < pm[j].HostPort } return pm[i].ContainerPort < pm[j].ContainerPort }) displayed = append(displayed, displayTap{ Name: s, IfIndex: tap.Index, Used: link.Attrs().RawFlags&unix.IFF_LOWER_UP > 0, InCubeVS: incubevs, PortMapping: pm, }) } sort.Slice(displayed, func(i, j int) bool { return displayed[i].Name < displayed[j].Name }) for _, tap := range displayed { fmt.Fprintf(w, "%s\t%t\t%t\t%d\t%v\n", tap.Name, tap.Used, tap.InCubeVS, tap.IfIndex, displayPortMapping(tap.PortMapping)) } return w.Flush() }, }
Functions ¶
func ContainerStatus ¶
func ContainerStatus(ctx gocontext.Context, c containerd.Container) string
func TimeSinceInHuman ¶
Types ¶
type StdinCloser ¶
type StdinCloser struct {
// contains filtered or unexported fields
}
func (*StdinCloser) SetCloser ¶
func (s *StdinCloser) SetCloser(closer func())
Click to show internal directories.
Click to hide internal directories.