Accept user Input os.stdin to container using Golang docker SDK - Interactive Container

亡梦爱人 提交于 2020-01-15 07:27:09

问题


My last resort is asking here. I'm new to Golang and I've made simple programs.

I'm trying to do the following: Using golang: 1 - run a container 2 - accept input stdin to the container

The example I want to use is the hashicorp/terraform docker image, I want to do a simple terraform apply but I need to wait for user input

below is the code I have working so far...anyone trying the exact code below needs to update the AWS environment variables or change the terraform test file to another provider...or just use a different docker image ;-)

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)


}

My example terraform file 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"
  }
}

so if I build that go file and run something like

./build apply with the test.tf in the same directory

I get the below output:


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

What I've been trying to figure out is how to wait for user input.

The logs are printing after the container is run and exits I think.. so I believe I need to use a mixture of these:

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

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

I just dont know how to implement these and there are no examples.

Any ideas would be helpful. I dont want the full answer I just want the general direction on how to use the container/stream and/or Client.ContainerAttach to wait for user input

Thanks so much!

EDIT:

I've managed to get it working. Below is the working code

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:
    }


}

来源:https://stackoverflow.com/questions/58732588/accept-user-input-os-stdin-to-container-using-golang-docker-sdk-interactive-co

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!