gpt4 book ai didi

docker - 使用 Golang docker SDK - Interactive Container 接受用户输入 os.stdin 到容器

转载 作者:行者123 更新时间:2023-12-01 19:20:12 30 4
gpt4 key购买 nike

我最后的手段是在这里问。我是 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, resp.ID, container.WaitConditionNotRunning)
select {
case err := <-errCh:
if err != nil {
panic(err)
}
case <-statusCh:
}




out, err := cli.ContainerLogs(ctx, resp.ID, 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, 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)
}
go io.Copy(os.Stdout, reader)


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

resp, err := cli.ContainerCreate(ctx, &container.Config{
AttachStderr:true,
AttachStdin: true,
Tty: true,
AttachStdout:true,
OpenStdin: true,
Cmd: os.Args[1:],
Image: "hashicorp/terraform",
WorkingDir: workingDir,
Env: []string{"AWS_ACCESS_KEY_ID=",
"AWS_SECRET_ACCESS_KEY=",
"AWS_SESSION_TOKEN=",

},
},
&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)
}



waiter, err := cli.ContainerAttach(ctx, resp.ID, 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, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
select {
case err := <-errCh:
if err != nil {
panic(err)
}
case <-statusCh:
}


}

最佳答案

对您问题的编辑非常有帮助,所以我只想更新对我有用的内容。
我在使用您的 Stdin 解决方案时遇到了问题(与 Scanner 和 waiter.Conn 有关),并采纳了 nicerobot 的建议来使用 io.Copy,它在 Docker 的 cli 源代码中使用。
为了获得终端的感觉(从标准输入、自动完成等中删除回声),我引用了这个线程: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)

关于docker - 使用 Golang docker SDK - Interactive Container 接受用户输入 os.stdin 到容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58732588/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com