Принять ввод пользователем os.stdin в контейнер с помощью Golang Docker SDK - Интерактивный контейнер - PullRequest
0 голосов
/ 06 ноября 2019

Мое последнее средство спрашивает здесь. Я новичок в Голанге, и я сделал простые программы.

Я пытаюсь сделать следующее: Использование golang: 1 - запустить контейнер 2 - принять ввод stdin для контейнера

Пример, который я хочу использовать, - это изображение докера hashicorp / terraformЯ хочу сделать простое terraform apply, но мне нужно подождать, пока пользователь введет

. Ниже приведен код, с которым я работаю до сих пор ... Любой, кто пытается получить точный код ниже, должен обновить переменные среды AWS илиизмените тестовый файл terraform на другого провайдера ... или просто используйте другой образ докера ;-)

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

, поэтому, если я соберукоторый идет файл и запустить что-то вроде

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


}
...