Coder Social home page Coder Social logo

Comments (33)

fsouza avatar fsouza commented on April 28, 2024

Hi @gravis, sorry about the delay.

The testing server is intended to be used in the same place as an actual server, so you can point a Client instance to it. Something like:

server, err := testing.NewServer("127.0.0.1:0", nil)
// ...
client, err := docker.NewClient(server.URL())
// use the client

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

Ok, but how do you use the wait command then? The container is stuck
until timeout. :(
Thanks

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

You would need to call Stop + Wait. I think we have something like this in tsuru, please hold on.

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

Just confirmed: we have a gorountine that stops the container and automatically "unblocks" the WaitContainer call.

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

Ok, thanks. It would be nice if you could share some code here, I'm still very confused.

I'm trying to test a command (ie: entrypoint) inside a container, in pseudo code, it's something like:

CreateContainer
StartContainer
WaitContainer
CopyFilesFromContainer
RemoveContainer

If I run a go routine to stop the container, how can I have hands on the resulting files inside the container? AFAIU, stopping the container will just emulate a timeout :(

Thanks

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

I see. You will need to manually stop the container anyway, don't you? I mean, if methods weren't private in the testing server, how would you write your test?

Instead of using a goroutine, you could stop the container at any time, the time that you would access the testing server and mark the container as stopped, unleashing the WaitContainer call.

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

Currently, I'm creating a real http server, with methods like:

mux.HandleFunc(fmt.Sprintf("/containers/%s/attach", container_id), func(w http.ResponseWriter, r *http.Request) {
        outStream := utils.NewStdWriter(w, utils.Stdout)

        fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
        fmt.Fprint(outStream, requires_txt)
    })

Maybe being able to pass my own handlers would be enough.
Otherwise, I would need:

  • A func to get the list of running containers (I should have only one)-
  • A func to stop the container (currently private func)
  • A func to set the output of a container (attach a io.Reader to all new created containers?)

What do you think?

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

Hmm, this is interesting. Do you thing that making the server extensible, so you can specify the handler for an operation is enough?

What about the API? Something like:

Server.CustomHandler(path string, handler http.Handler)

Should the path be a regular expression or the actual path?

In your case, it would be:

server.CustomHandler(fmt.Sprintf("/containers/%s/attach", container_id), func(w http.ResponseWriter, r *http.Request) {
        outStream := utils.NewStdWriter(w, utils.Stdout)

        fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
        fmt.Fprint(outStream, requires_txt)
    })

What do you think?

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

Sounds like a terrific idea. I still need a func to get the container_id btw.
What would be more awesome is to pass something like chan *docker.Container (buffered) to NewServer, and then send the container on the channel when created. I would be able to wait exactly the time needed, and get the container_id directly :)

But I think we're heading in the right direction, and I really appreciate your open mind :)

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

What about:

http://play.golang.org/p/7dLcZ5ksuW

:)

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

Nice snippet, cChan should be exported, right?

Also, the channel should be fed when the client calls CreateContainer, and not on NewServer.

What do you think? Sounds fair?

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

Sure, it was a proof of concept. I'm very glad you like it
Indeed, cChan should be exported, and the container sent on create, not on server creation :)
We mustn't forget to close the chan when server.Close() is called btw.

Thanks!

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

Sorry for the huge delay. I'm planning to do it, but can you confirm that this flow will be enough for your use case?

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

No problem at all. Yes, I think this would be really make it. If I find blocking issues, I will propose PRs anymay :)

Thanks a lot

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

Can you take a look at #113? Thanks!

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

I've just pushed the CustomHandler method, can you take a look? :) Is it good?

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

Damned, you're fast :)
Will take a look this week-end, I promise!

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

No hurry man :) I was wondering: should we add a method to remove the custom handler?

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

Sorry, I still need some time to test this. I'm fighting a nasty bug with AttachContainer, hitting 100% CPU when getting container logs, and blocking all my routines :(

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

No problem. Is it the testing server or the real server? Is the client or the server hitting 100% CPU? I may help debugging it, if you have a reproducer. :)

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

I have opened a ticket actually: #114
It's painfull to debug, because it occurs very randomly :(

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

@gravis thanks for reporting it!

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

I'm wondering if it's the good approach now. I have started to replace my fake docker with testing.DockerServer, and ended up with copying/pasting the content of https://github.com/fsouza/go-dockerclient/blob/master/testing/server.go#L397, just to be able to replace the 2 last lines...

There's probably something else to explore. Maybe a CustomAttachOutput, instead of overriding the whole container method?

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

@gravis what about something like SetContainerOutput?

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

sounds good! Sorry for changing target like that. It's only when you have hands in the code that you realize how the API should behave... :(

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

I also have some files I need to write in the container, I don't think there a method to add files in a container ? If not, I will probably need also something like: AddFileToContainer(r io.Reader, path string) error

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

@gravis how would you access these files?

Currently, "go-dockerclient" doesn't support copying files from the container. Wouldn't you need a CopyFiles method as well?

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

From the container? You mean, TO the container, right? If it is TO the container, yes, it will be needed :)

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

Sorry, I meant from the container, but I think I'm misunderstanding your use case.

Will you insert files in the container in the testing server? What's the purpose? I mean, the test code would look like:

runTheServer()
insertFileInTheContainer()
callTheProductionCode()

What does the production code do?

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

Ok, to sum up. We have containers to run isolated processes. Theses commands will generate some output (container logs), and some files to be copied from the container, and sent to another API.
To be able to test, I currently create a fake server (see below). It would be an improvement to use something closer to docker, like the testing server.

requires_txt := "Flask==0.8\nJinja2==2.6"

    mux := http.NewServeMux()

    // CreateContainer stub
    container_id := "5fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
    mux.HandleFunc(fmt.Sprintf("/containers/create"), func(w http.ResponseWriter, r *http.Request) {
        jsonContainer := fmt.Sprintf(`{
    "Id": "%s",
    "Warnings": []
    }`, container_id)
        fmt.Fprint(w, jsonContainer)
    })

    // StartContainer stub
    mux.HandleFunc(fmt.Sprintf("/containers/%s/start", container_id), func(w http.ResponseWriter, r *http.Request) {})

    // WaitContainer stub
    mux.HandleFunc(fmt.Sprintf("/containers/%s/wait", container_id), func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, `{"StatusCode": 0}`)
    })

    mux.HandleFunc(fmt.Sprintf("/containers/%s/attach", container_id), func(w http.ResponseWriter, r *http.Request) {
        outStream := utils.NewStdWriter(w, utils.Stdout)

        fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
        fmt.Fprint(outStream, requires_txt)
    })

    // CopyFromContainer stub
    mux.HandleFunc(fmt.Sprintf("/containers/%s/copy", container_id), func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/octet-stream")
        // Create a buffer to write our archive to.
        buf := new(bytes.Buffer)

        // Create a new tar archive.
        tw := tar.NewWriter(buf)

        // Add some files to the archive.
        var files = []struct {
            Name, Body string
        }{
            {"requires.txt", requires_txt},
        }
        for _, file := range files {
            hdr := &tar.Header{
                Name: file.Name,
                Size: int64(len(file.Body)),
            }
            if err := tw.WriteHeader(hdr); err != nil {
                log.Fatal(err)
            }
            if _, err := tw.Write([]byte(file.Body)); err != nil {
                log.Fatal(err)
            }
        }
        // Make sure to check the error on Close.
        if err := tw.Close(); err != nil {
            log.Fatal(err)
        }
        if _, err := io.Copy(w, buf); err != nil {
            // return err
        }
    })
    server := httptest.NewServer(mux)

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

I understand now. In order to insert files into the container or customise the output, the container would have to exist, so you'd need to be able to call the methods SetContainerOutput and AddFileToContainer after the CreateContainer call, but before the Logs/Attach and CopyFromContainer calls.

Am I understanding it right? I think this is a scenario that is quite hard to test.

from go-dockerclient.

gravis avatar gravis commented on April 28, 2024

You're 100% right :)

from go-dockerclient.

fsouza avatar fsouza commented on April 28, 2024

I don't think you would be able to guarantee such order. You'd need some mechanism to "pre-register" the container, and have CreateContainer returning that container. Something like:

container, err := server.PrepareContainerForCreation()
server.SetContainerOutput(container, "something")
server.AddFileToTheContainer(container, "/filename", "file content")
server.AddFileToTheContainer(container, "/etc/filename", "file content")
server.AddFileToTheContainer(container, "/tmp/filename", "file content")
<call production code>
<do assertions>

Then the production code will call CreateContainer, StartContainer, WaitContainer, AttachToContainer and CopyFromContainer, using the container returned by CreateContainer, that is the container previously prepared for creation. This would enable us to prepare the behaviour of a container before creating it. Also, as far as I can tell, we would not need the custom handler anymore.

What do you think?

from go-dockerclient.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.