与用户交互的交互式容器中,通过 Golang docker SDK 启用 os.stdin 接收用户输入
来源:stackoverflow
时间:2024-03-22 13:36:59 466浏览 收藏
在使用 Golang Docker SDK 与容器交互时,通过启用 `os.stdin` 接收用户输入至关重要。本文介绍了如何使用 `docker/docker` 库中的 `client.containerattach` 函数来实现这一目标。该解决方案涉及创建会话流,通过该流容器可以与用户交互,等待用户输入并将其发送到容器。通过遵循提供的代码示例,开发人员可以实现交互式容器,允许用户在运行时提供输入。
我的最后一招是在这里问。我是 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 相关)时遇到问题,并采纳了 nierobot 的建议来使用 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)以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于Golang的相关知识,也可关注golang学习网公众号。
-
502 收藏
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
139 收藏
-
204 收藏
-
325 收藏
-
478 收藏
-
486 收藏
-
439 收藏
-
357 收藏
-
352 收藏
-
101 收藏
-
440 收藏
-
212 收藏
-
143 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习