使用Golang docker SDK将用户输入os.stdin接受到容器-交互式容器

我的最后一招是在这里问。我是Golang的新手,我已经编写了简单的程序。

我正在尝试执行以下操作: 使用golang: 1-运行一个容器 2-接受输入标准输入到容器

我要使用的示例是hashicorp / terraform docker映像,我想做一个简单的terraform apply,但是我需要等待用户输入

下面是我到目前为止正在使用的代码...尝试以下确切代码的任何人都需要更新AWS环境变量或将terraform测试文件更改为另一个提供程序...或仅使用其他docker镜像;-)

package main

import (
    "fmt"
    "github.com/docker/docker/api/types"
    "github.com/docker/docker/api/types/container"
    "github.com/docker/docker/api/types/mount"
    "github.com/docker/docker/client"
    "github.com/docker/docker/pkg/stdcopy"
    "golang.org/x/net/context"
    "io"
    "os"
)

const workingDir = "/home"


func main() {
    ctx := context.Background()
    cli,err := client.NewClientWithOpts(client.FromEnv,client.WithAPIVersionNegotiation())
    if err != nil {
        panic(err)
    }

    reader,err := cli.ImagePull(ctx,"hashicorp/terraform",types.ImagePullOptions{})
    if err != nil {
        panic(err)
    }
    io.Copy(os.Stdout,reader)

    fmt.Println(os.Args)
    cwd,_ := os.Getwd()

    resp,err := cli.ContainerCreate(ctx,&container.Config{
        AttachStdin: true,Tty:         false,StdinOnce:   true,AttachStdout:true,Cmd:         os.Args[1:],Image:       "hashicorp/terraform",WorkingDir:   workingDir,Env:         []string{"AWS_accESS_KEY_ID=XXX","AWS_SECRET_accESS_KEY=XXX","AWS_SESSION_TOKEN=XXX"},},&container.HostConfig{

            Mounts: []mount.Mount{
                mount.Mount{
                    Type: mount.TypeBind,Source: cwd,Target: workingDir,nil,"")
    if err != nil {
        panic(err)
    }

    if err := cli.ContainerStart(ctx,resp.ID,types.ContainerStartOptions{}); err != nil {
        panic(err)
    }

    statusCh,errCh := cli.ContainerWait(ctx,container.WaitConditionNotRunning)
    select {
    case err := <-errCh:
        if err != nil {
            panic(err)
        }
    case <-statusCh:
    }




    out,err := cli.ContainerLogs(ctx,types.ContainerLogsOptions{ShowStdout: true})
    if err != nil {
        panic(err)
    }

    stdcopy.StdCopy(os.Stdout,os.Stderr,out)


}

我的示例Terraform文件test.tf

provider "aws" {
  region                  = "eu-west-1"
}


resource "aws_vpc" "main" {
  cidr_block       = "10.0.0.0/16"
  instance_tenancy = "dedicated"

  tags = {
    Name = "test-main-vpc"
  }
}

因此,如果我构建该go文件并运行

./build apply和test.tf在同一目录中

我得到以下输出:


An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_vpc.main will be created
  + resource "aws_vpc" "main" {
      + arn                              = (known after apply)
      + assign_generated_ipv6_cidr_block = false
      + cidr_block                       = "10.0.0.0/16"
      + default_network_acl_id           = (known after apply)
      + default_route_table_id           = (known after apply)
      + default_security_group_id        = (known after apply)
      + dhcp_options_id                  = (known after apply)
      + enable_classiclink               = (known after apply)
      + enable_classiclink_dns_support   = (known after apply)
      + enable_dns_hostnames             = (known after apply)
      + enable_dns_support               = true
      + id                               = (known after apply)
      + instance_tenancy                 = "dedicated"
      + ipv6_association_id              = (known after apply)
      + ipv6_cidr_block                  = (known after apply)
      + main_route_table_id              = (known after apply)
      + owner_id                         = (known after apply)
      + tags                             = {
          + "Name" = "test-main-vpc"
        }
    }

Plan: 1 to add,0 to change,0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: 
Apply cancelled.

Process finished with exit code 0

我一直想弄清楚的是如何等待用户输入。

运行容器并退出后,日志正在打印,我认为..所以我认为我需要混合使用以下这些容器:

https://godoc.org/github.com/docker/docker/container/stream

https://godoc.org/github.com/docker/docker/client#Client.ContainerAttach

我只是不知道如何实现这些,因此没有示例。

任何想法都会有所帮助。我不想要完整的答案,我只想了解如何使用容器/流和/或Client.ContainerAttach等待用户输入的一般指导

非常感谢!

编辑:

我设法使其正常运行。下面是工作代码

package main

import (
    "bufio"
    "fmt"
    "github.com/docker/docker/api/types"
    "github.com/docker/docker/api/types/container"
    "github.com/docker/docker/api/types/mount"
    "github.com/docker/docker/client"
    "golang.org/x/net/context"
    "io"
    "os"
)

const workingDir = "/home"
var inout chan []byte

func main() {
    inout = make(chan []byte)

    ctx := context.Background()
    cli,types.ImagePullOptions{})
    if err != nil {
        panic(err)
    }
    go io.Copy(os.Stdout,reader)


    //fmt.Println(os.Args)
    cwd,&container.Config{
        AttachStderr:true,AttachStdin: true,Tty:         true,OpenStdin:   true,Env:         []string{"AWS_accESS_KEY_ID=","AWS_SECRET_accESS_KEY=","AWS_SESSION_TOKEN=","")
    if err != nil {
        panic(err)
    }


    if err := cli.ContainerStart(ctx,types.ContainerStartOptions{}); err != nil {
        panic(err)
    }



    waiter,err := cli.ContainerAttach(ctx,types.ContainerAttachOptions{
        Stderr:       true,Stdout:       true,Stdin:        true,Stream:       true,})

    go  io.Copy(os.Stdout,waiter.Reader)
    go  io.Copy(os.Stderr,waiter.Reader)

    if err != nil {
        panic(err)
    }

    go func() {
        scanner := bufio.NewScanner(os.Stdin)
        for scanner.Scan() {
            inout <- []byte(scanner.Text())
        }
    }()

    // Write to docker container
    go func(w io.Writecloser) {
        for {
            data,ok := <-inout
            //log.Println("Received to send to docker",string(data))
            if !ok {
                fmt.Println("!ok")
                w.Close()
                return
            }

            w.Write(append(data,'\n'))
        }
    }(waiter.Conn)


    statusCh,container.WaitConditionNotRunning)
    select {
    case err := <-errCh:
        if err != nil {
            panic(err)
        }
    case <-statusCh:
    }


}
jakela 回答:使用Golang docker SDK将用户输入os.stdin接受到容器-交互式容器

对您的问题的编辑非常有帮助,所以我只想更新对我有用的内容。

我在使用您的Stdin解决方案时遇到问题(与Scanner和waiter.Conn有关),并接受nicerobot的建议使用Docker.cli源代码中使用的io.Copy。

要获得终端感觉(消除stdin,自动完成等的回声),我引用了以下线程:https://github.com/fsouza/go-dockerclient/issues/707,该线程基本上说您必须将stdin视为原始终端(后来意识到这也是在Docker的cli源代码)。同样重要的是要注意,您必须尽快恢复终端状态,否则其他打印语句可能会出现问题。

最后,我的最终代码看起来像这样……

// ContainerCreate
    
// ContainerAttach

go io.Copy(os.Stdout,waiter.Reader)
go io.Copy(os.Stderr,waiter.Reader)
go io.Copy(waiter.Conn,os.Stdin)

// ContainerStart

// import("golang.org/x/net/context")
fd := int(os.Stdin.Fd())
var oldState *terminal.State
if terminal.IsTerminal(fd) {
  oldState,err = terminal.MakeRaw(fd)
  if err != nil {
    // print error
  }
  defer terminal.Restore(fd,oldState)
}

// ContainerWait for exit

terminal.Restore(fd,oldState)
本文链接:https://www.f2er.com/3151295.html

大家都在问