|
| 1 | +package client |
| 2 | + |
| 3 | +import ( |
| 4 | + "bytes" |
| 5 | + "encoding/json" |
| 6 | + "fmt" |
| 7 | + "io" |
| 8 | + "net/url" |
| 9 | + "text/tabwriter" |
| 10 | + "text/template" |
| 11 | + |
| 12 | + "github.com/docker/docker/api/types" |
| 13 | + Cli "github.com/docker/docker/cli" |
| 14 | + "github.com/docker/docker/opts" |
| 15 | + flag "github.com/docker/docker/pkg/mflag" |
| 16 | + "github.com/docker/docker/pkg/parsers/filters" |
| 17 | +) |
| 18 | + |
| 19 | +// CmdVolume is the parent subcommand for all volume commands |
| 20 | +// |
| 21 | +// Usage: docker volume <COMMAND> <OPTS> |
| 22 | +func (cli *DockerCli) CmdVolume(args ...string) error { |
| 23 | + description := "Manage Docker volumes\n\nCommands:\n" |
| 24 | + commands := [][]string{ |
| 25 | + {"create", "Create a volume"}, |
| 26 | + {"inspect", "Return low-level information on a volume"}, |
| 27 | + {"ls", "List volumes"}, |
| 28 | + {"rm", "Remove a volume"}, |
| 29 | + } |
| 30 | + |
| 31 | + for _, cmd := range commands { |
| 32 | + description += fmt.Sprintf(" %-25.25s%s\n", cmd[0], cmd[1]) |
| 33 | + } |
| 34 | + |
| 35 | + description += "\nRun 'docker volume COMMAND --help' for more information on a command." |
| 36 | + cmd := Cli.Subcmd("volume", []string{"[COMMAND]"}, description, true) |
| 37 | + cmd.ParseFlags(args, true) |
| 38 | + |
| 39 | + return cli.CmdVolumeLs(args...) |
| 40 | +} |
| 41 | + |
| 42 | +// CmdVolumeLs outputs a list of Docker volumes. |
| 43 | +// |
| 44 | +// Usage: docker volume ls [OPTIONS] |
| 45 | +func (cli *DockerCli) CmdVolumeLs(args ...string) error { |
| 46 | + cmd := Cli.Subcmd("volume ls", nil, "List volumes", true) |
| 47 | + |
| 48 | + quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display volume names") |
| 49 | + flFilter := opts.NewListOpts(nil) |
| 50 | + cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'dangling=true')") |
| 51 | + |
| 52 | + cmd.Require(flag.Exact, 0) |
| 53 | + cmd.ParseFlags(args, true) |
| 54 | + |
| 55 | + volFilterArgs := filters.Args{} |
| 56 | + for _, f := range flFilter.GetAll() { |
| 57 | + var err error |
| 58 | + volFilterArgs, err = filters.ParseFlag(f, volFilterArgs) |
| 59 | + if err != nil { |
| 60 | + return err |
| 61 | + } |
| 62 | + } |
| 63 | + |
| 64 | + v := url.Values{} |
| 65 | + if len(volFilterArgs) > 0 { |
| 66 | + filterJSON, err := filters.ToParam(volFilterArgs) |
| 67 | + if err != nil { |
| 68 | + return err |
| 69 | + } |
| 70 | + v.Set("filters", filterJSON) |
| 71 | + } |
| 72 | + |
| 73 | + resp, err := cli.call("GET", "/volumes?"+v.Encode(), nil, nil) |
| 74 | + if err != nil { |
| 75 | + return err |
| 76 | + } |
| 77 | + |
| 78 | + var volumes types.VolumesListResponse |
| 79 | + if err := json.NewDecoder(resp.body).Decode(&volumes); err != nil { |
| 80 | + return err |
| 81 | + } |
| 82 | + |
| 83 | + w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) |
| 84 | + if !*quiet { |
| 85 | + fmt.Fprintf(w, "DRIVER \tVOLUME NAME") |
| 86 | + fmt.Fprintf(w, "\n") |
| 87 | + } |
| 88 | + |
| 89 | + for _, vol := range volumes.Volumes { |
| 90 | + if *quiet { |
| 91 | + fmt.Fprintln(w, vol.Name) |
| 92 | + continue |
| 93 | + } |
| 94 | + fmt.Fprintf(w, "%s\t%s\n", vol.Driver, vol.Name) |
| 95 | + } |
| 96 | + w.Flush() |
| 97 | + return nil |
| 98 | +} |
| 99 | + |
| 100 | +// CmdVolumeInspect displays low-level information on one or more volumes. |
| 101 | +// |
| 102 | +// Usage: docker volume inspect [OPTIONS] VOLUME [VOLUME...] |
| 103 | +func (cli *DockerCli) CmdVolumeInspect(args ...string) error { |
| 104 | + cmd := Cli.Subcmd("volume inspect", []string{"[VOLUME NAME]"}, "Return low-level information on a volume", true) |
| 105 | + tmplStr := cmd.String([]string{"f", "-format"}, "", "Format the output using the given go template.") |
| 106 | + if err := cmd.Parse(args); err != nil { |
| 107 | + return nil |
| 108 | + } |
| 109 | + |
| 110 | + cmd.Require(flag.Min, 1) |
| 111 | + cmd.ParseFlags(args, true) |
| 112 | + |
| 113 | + var tmpl *template.Template |
| 114 | + if *tmplStr != "" { |
| 115 | + var err error |
| 116 | + tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr) |
| 117 | + if err != nil { |
| 118 | + return err |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + var status = 0 |
| 123 | + var volumes []*types.Volume |
| 124 | + for _, name := range cmd.Args() { |
| 125 | + resp, err := cli.call("GET", "/volumes/"+name, nil, nil) |
| 126 | + if err != nil { |
| 127 | + return err |
| 128 | + } |
| 129 | + |
| 130 | + var volume types.Volume |
| 131 | + if err := json.NewDecoder(resp.body).Decode(&volume); err != nil { |
| 132 | + fmt.Fprintf(cli.err, "%s\n", err) |
| 133 | + status = 1 |
| 134 | + continue |
| 135 | + } |
| 136 | + |
| 137 | + if tmpl == nil { |
| 138 | + volumes = append(volumes, &volume) |
| 139 | + continue |
| 140 | + } |
| 141 | + |
| 142 | + if err := tmpl.Execute(cli.out, &volume); err != nil { |
| 143 | + if err := tmpl.Execute(cli.out, &volume); err != nil { |
| 144 | + fmt.Fprintf(cli.err, "%s\n", err) |
| 145 | + status = 1 |
| 146 | + continue |
| 147 | + } |
| 148 | + } |
| 149 | + io.WriteString(cli.out, "\n") |
| 150 | + } |
| 151 | + |
| 152 | + if tmpl != nil { |
| 153 | + return nil |
| 154 | + } |
| 155 | + |
| 156 | + b, err := json.MarshalIndent(volumes, "", " ") |
| 157 | + if err != nil { |
| 158 | + return err |
| 159 | + } |
| 160 | + _, err = io.Copy(cli.out, bytes.NewReader(b)) |
| 161 | + if err != nil { |
| 162 | + return err |
| 163 | + } |
| 164 | + io.WriteString(cli.out, "\n") |
| 165 | + |
| 166 | + if status != 0 { |
| 167 | + return Cli.StatusError{StatusCode: status} |
| 168 | + } |
| 169 | + return nil |
| 170 | +} |
| 171 | + |
| 172 | +// CmdVolumeCreate creates a new container from a given image. |
| 173 | +// |
| 174 | +// Usage: docker volume create [OPTIONS] |
| 175 | +func (cli *DockerCli) CmdVolumeCreate(args ...string) error { |
| 176 | + cmd := Cli.Subcmd("volume create", nil, "Create a volume", true) |
| 177 | + flDriver := cmd.String([]string{"d", "-driver"}, "local", "Specify volume driver name") |
| 178 | + flName := cmd.String([]string{"-name"}, "", "Specify volume name") |
| 179 | + |
| 180 | + flDriverOpts := opts.NewMapOpts(nil, nil) |
| 181 | + cmd.Var(flDriverOpts, []string{"o", "-opt"}, "Set driver specific options") |
| 182 | + |
| 183 | + cmd.Require(flag.Exact, 0) |
| 184 | + cmd.ParseFlags(args, true) |
| 185 | + |
| 186 | + volReq := &types.VolumeCreateRequest{ |
| 187 | + Driver: *flDriver, |
| 188 | + DriverOpts: flDriverOpts.GetAll(), |
| 189 | + } |
| 190 | + |
| 191 | + if *flName != "" { |
| 192 | + volReq.Name = *flName |
| 193 | + } |
| 194 | + |
| 195 | + resp, err := cli.call("POST", "/volumes", volReq, nil) |
| 196 | + if err != nil { |
| 197 | + return err |
| 198 | + } |
| 199 | + |
| 200 | + var vol types.Volume |
| 201 | + if err := json.NewDecoder(resp.body).Decode(&vol); err != nil { |
| 202 | + return err |
| 203 | + } |
| 204 | + fmt.Fprintf(cli.out, "%s\n", vol.Name) |
| 205 | + return nil |
| 206 | +} |
| 207 | + |
| 208 | +// CmdVolumeRm removes one or more containers. |
| 209 | +// |
| 210 | +// Usage: docker volume rm VOLUME [VOLUME...] |
| 211 | +func (cli *DockerCli) CmdVolumeRm(args ...string) error { |
| 212 | + cmd := Cli.Subcmd("volume rm", []string{"[NAME]"}, "Remove a volume", true) |
| 213 | + cmd.Require(flag.Min, 1) |
| 214 | + cmd.ParseFlags(args, true) |
| 215 | + |
| 216 | + var status = 0 |
| 217 | + for _, name := range cmd.Args() { |
| 218 | + _, err := cli.call("DELETE", "/volumes/"+name, nil, nil) |
| 219 | + if err != nil { |
| 220 | + fmt.Fprintf(cli.err, "%s\n", err) |
| 221 | + status = 1 |
| 222 | + continue |
| 223 | + } |
| 224 | + fmt.Fprintf(cli.out, "%s\n", name) |
| 225 | + } |
| 226 | + |
| 227 | + if status != 0 { |
| 228 | + return Cli.StatusError{StatusCode: status} |
| 229 | + } |
| 230 | + return nil |
| 231 | +} |
0 commit comments