Golang : Volume Lifecycle

In this lab we learn how to use golang to create and manage volumes in Docker.

Create a Container with a Volume

  1. Create a Map object Config which has details about the image and volume binding path/

    config := map[string]interface{}{
                    "Image":     "busybox",
                    "OpenStdin": true,
                    "Volumes": map[string]struct{}{"/tmp": {}},
            }
    
  2. Pass this object to SockRequest(..) which POSTS to the default socket endpoint.

_, body, err := sampleutils.SockRequest("POST",
        "/containers/create?name="+name, config)
  1. Function with the complete code listing. Parameter name is the name of the container which is passed to the function

    func CreateContainerWithVolume(name string) {
            config := map[string]interface{}{
                    "Image":     "busybox",
                    "OpenStdin": true,
                    "Volumes": map[string]struct{}{"/tmp": {}},
            }
            _, body, err := sampleutils.SockRequest("POST",
                    "/containers/create?name="+name, config)
            if err != nil {
                    fmt.Printf("Error %v\n", err)
    
            } else {
                    var resp *sampleutils.ResponseCreateContainer
                    if err = json.Unmarshal(body, &resp); err != nil {
                            fmt.Printf("unable to unmarshal response body: %v\n", err)
                    }
                    sampleutils.PrettyPrint(resp)
            }
    }
    
  2. Call the function CreateContainerWithVolume(name string) from the main() function

    func main() {
            if len(os.Args) > 1 {
                    arg := os.Args[1]
                    name := arg
                    CreateContainerWithVolume(name)
            } else {
                    fmt.Printf("Please specify container name on the command line\n")
            }
    }
    
  1. Execute the program

    $ go run dockersamples/create_container_with_volume.go test3_vol
    
  2. You can start the container and inspect the container to make sure volume got bound in the specifed path.

    $ docker inspect test3_vol
    
    [{
        "AppArmorProfile": "",
        "Args": [],
        "Config": {
            ..
            "Cmd": [
                "/bin/sh"
            ],
        "Volumes": {
            "/tmp": "/var/lib/docker/vfs/dir/ad64baef817f1..08b6584881427c29214f"
        },
        "VolumesRW": {
            "/tmp": true
        }
    }
    ]
    

Create a Container with Volume Binds

In this sample we bind a directory on the host (created on the fly) with a /tmp volume mounted in the container.

  1. Create a Map object Config which has details about the image and volume binding path/

    config := map[string]interface{}{
                    "Image":     "busybox",
                    "Volumes":   map[string]struct{}{"/tmp": {}},
                    "OpenStdin": true,
            }
    
  2. Pass this object to SockRequest(..) which does a Http POST to the default socket endpoint.

_, body, err := sampleutils.SockRequest("POST",
        "/containers/create?name="+name, config)
  1. Create a bindPath in a random Host directory and pass it while starting the Container through config object. In our case this directory is /tmp/test.yJzTpXFbfH

    bindPath := sampleutils.RandomUnixTmpDirPath("test")
    

    Please refer to util.go. for more details about RandomUnixTmpDirPath(..)

    In the codeblock below notice how a Map specifying relationship between bindPath and /tmp is created. Finally samplesutil.SockRequest to start the container is called with the config.

    config = map[string]interface{}{
                    "Binds": []string{bindPath + ":/tmp"},
    }
    containerId := sampleutils.GetContainerId(name)
    status, _, err = sampleutils.SockRequest("POST", "/containers/"+containerId+"/start", config)
    
  1. Function with the complete code listing. Parameter name is the name of the container which is passed to the function

    func CreateContainerWithVolumeBinds(name string) {
            config := map[string]interface{}{
                    "Image":     "busybox",
                    "Volumes":   map[string]struct{}{"/tmp": {}},
                    "OpenStdin": true,
            }
    
            status, _, err := sampleutils.SockRequest("POST", "/containers/create?name="+name, config)
            fmt.Printf("status: %v\n", status)
            if err != nil {
                    fmt.Printf("Error while creating the Container: %v\n", err)
                    return
            }
    
            bindPath := sampleutils.RandomUnixTmpDirPath("test")
            fmt.Printf("BindPath: %v\n", bindPath)
    
            config = map[string]interface{}{
                    "Binds": []string{bindPath + ":/tmp"},
            }
            containerId := sampleutils.GetContainerId(name)
            status, _, err = sampleutils.SockRequest("POST",
                    "/containers/"+containerId+"/start", config)
            fmt.Printf("Status of the call: %v\n", status)
    }
    
  2. Call the function CreateContainerWithVolumeBinds(name string) from the main() function

    func main() {
            if len(os.Args) > 1 {
                    arg := os.Args[1]
                    name := arg
                    CreateContainerWithVolumeBinds(name)
            } else {
                    fmt.Printf("Please specify container name on the command line\n")
            }
    }
    
  3. Execute the program

    $ go run dockersamples/create_container_with_volume_binds.go test4_vol
    
  1. Container will be up and running, inspect the container to the check the bind created.

    $ docker inspect test4_vol
    
    [{
        "AppArmorProfile": "",
        "Args": [],
        "Config": {
            ..
            "Cmd": [
                "/bin/sh"
            ],
       "Volumes": {
           "/tmp": "/tmp/test.yJzTpXFbfH"
       },
       "VolumesRW": {
           "/tmp": true
       }
    }
    ]