mirror of https://github.com/docker/compose.git
Add volumes to run command
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
This commit is contained in:
parent
7603a3b832
commit
b25a6b4bd6
|
@ -155,17 +155,25 @@ func (cs *aciContainerService) Run(ctx context.Context, r containers.ContainerCo
|
||||||
Published: p.HostPort,
|
Published: p.HostPort,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
projectVolumes, serviceConfigVolumes, err := convert.GetRunVolumes(r.Volumes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
project := compose.Project{
|
project := compose.Project{
|
||||||
Name: r.ID,
|
Name: r.ID,
|
||||||
Config: types.Config{
|
Config: types.Config{
|
||||||
Services: []types.ServiceConfig{
|
Services: []types.ServiceConfig{
|
||||||
{
|
{
|
||||||
Name: singleContainerName,
|
Name: singleContainerName,
|
||||||
Image: r.Image,
|
Image: r.Image,
|
||||||
Ports: ports,
|
Ports: ports,
|
||||||
Labels: r.Labels,
|
Labels: r.Labels,
|
||||||
|
Volumes: serviceConfigVolumes,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Volumes: projectVolumes,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
package convert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetRunVolumes return volume configurations for a project and a single service
|
||||||
|
// this is meant to be used as a compose project of a single service
|
||||||
|
func GetRunVolumes(volumes []string) (map[string]types.VolumeConfig, []types.ServiceVolumeConfig, error) {
|
||||||
|
var serviceConfigVolumes []types.ServiceVolumeConfig
|
||||||
|
projectVolumes := make(map[string]types.VolumeConfig, len(volumes))
|
||||||
|
for i, v := range volumes {
|
||||||
|
var vi volumeInput
|
||||||
|
err := vi.parse(fmt.Sprintf("volume-%d", i), v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
projectVolumes[vi.name] = types.VolumeConfig{
|
||||||
|
Name: vi.name,
|
||||||
|
Driver: azureFileDriverName,
|
||||||
|
DriverOpts: map[string]string{
|
||||||
|
volumeDriveroptsAccountNameKey: vi.username,
|
||||||
|
volumeDriveroptsAccountKeyKey: vi.key,
|
||||||
|
volumeDriveroptsShareNameKey: vi.share,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
sv := types.ServiceVolumeConfig{
|
||||||
|
Type: azureFileDriverName,
|
||||||
|
Source: vi.name,
|
||||||
|
Target: vi.target,
|
||||||
|
}
|
||||||
|
serviceConfigVolumes = append(serviceConfigVolumes, sv)
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectVolumes, serviceConfigVolumes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type volumeInput struct {
|
||||||
|
name string
|
||||||
|
username string
|
||||||
|
key string
|
||||||
|
share string
|
||||||
|
target string
|
||||||
|
}
|
||||||
|
|
||||||
|
func scapeKeySlashes(rawURL string) (string, error) {
|
||||||
|
urlSplit := strings.Split(rawURL, "@")
|
||||||
|
if len(urlSplit) < 1 {
|
||||||
|
return "", errors.New("invalid url format " + rawURL)
|
||||||
|
}
|
||||||
|
userPasswd := strings.ReplaceAll(urlSplit[0], "/", "_")
|
||||||
|
scaped := userPasswd + rawURL[strings.Index(rawURL, "@"):]
|
||||||
|
|
||||||
|
return scaped, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unscapeKey(passwd string) string {
|
||||||
|
return strings.ReplaceAll(passwd, "_", "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes the second ':' that separates the source from target
|
||||||
|
func volumeURL(pathURL string) (*url.URL, error) {
|
||||||
|
scapedURL, err := scapeKeySlashes(pathURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pathURL = "//" + scapedURL
|
||||||
|
|
||||||
|
count := strings.Count(pathURL, ":")
|
||||||
|
if count > 2 {
|
||||||
|
return nil, fmt.Errorf("unable to parse volume mount %q", pathURL)
|
||||||
|
}
|
||||||
|
if count == 2 {
|
||||||
|
tokens := strings.Split(pathURL, ":")
|
||||||
|
pathURL = fmt.Sprintf("%s:%s%s", tokens[0], tokens[1], tokens[2])
|
||||||
|
}
|
||||||
|
return url.Parse(pathURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *volumeInput) parse(name string, s string) error {
|
||||||
|
volumeURL, err := volumeURL(s)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("volume specification %q could not be parsed %q", s, err)
|
||||||
|
}
|
||||||
|
v.username = volumeURL.User.Username()
|
||||||
|
if v.username == "" {
|
||||||
|
return fmt.Errorf("volume specification %q does not include a storage username", v)
|
||||||
|
}
|
||||||
|
passwd, ok := volumeURL.User.Password()
|
||||||
|
if !ok || passwd == "" {
|
||||||
|
return fmt.Errorf("volume specification %q does not include a storage key", v)
|
||||||
|
}
|
||||||
|
v.key = unscapeKey(passwd)
|
||||||
|
v.share = volumeURL.Host
|
||||||
|
if v.share == "" {
|
||||||
|
return fmt.Errorf("volume specification %q does not include a storage file share", v)
|
||||||
|
}
|
||||||
|
v.name = name
|
||||||
|
v.target = volumeURL.Path
|
||||||
|
if v.target == "" {
|
||||||
|
v.target = filepath.Join("/run/volumes/", v.share)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package run
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/api/containers"
|
||||||
|
)
|
||||||
|
|
||||||
|
type runOpts struct {
|
||||||
|
name string
|
||||||
|
publish []string
|
||||||
|
volumes []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func toPorts(ports []string) ([]containers.Port, error) {
|
||||||
|
var result []containers.Port
|
||||||
|
|
||||||
|
for _, port := range ports {
|
||||||
|
parts := strings.Split(port, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return nil, fmt.Errorf("unable to parse ports %q", port)
|
||||||
|
}
|
||||||
|
source, err := strconv.Atoi(parts[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
destination, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, containers.Port{
|
||||||
|
HostPort: uint32(source),
|
||||||
|
ContainerPort: uint32(destination),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runOpts) toContainerConfig(image string) (containers.ContainerConfig, error) {
|
||||||
|
publish, err := toPorts(r.publish)
|
||||||
|
if err != nil {
|
||||||
|
return containers.ContainerConfig{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return containers.ContainerConfig{
|
||||||
|
ID: r.name,
|
||||||
|
Image: image,
|
||||||
|
Ports: publish,
|
||||||
|
Volumes: r.volumes,
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -54,6 +54,7 @@ func Command() *cobra.Command {
|
||||||
cmd.Flags().StringArrayVarP(&opts.Publish, "publish", "p", []string{}, "Publish a container's port(s). [HOST_PORT:]CONTAINER_PORT")
|
cmd.Flags().StringArrayVarP(&opts.Publish, "publish", "p", []string{}, "Publish a container's port(s). [HOST_PORT:]CONTAINER_PORT")
|
||||||
cmd.Flags().StringVar(&opts.Name, "name", getRandomName(), "Assign a name to the container")
|
cmd.Flags().StringVar(&opts.Name, "name", getRandomName(), "Assign a name to the container")
|
||||||
cmd.Flags().StringArrayVarP(&opts.Labels, "label", "l", []string{}, "Set meta data on a container")
|
cmd.Flags().StringArrayVarP(&opts.Labels, "label", "l", []string{}, "Set meta data on a container")
|
||||||
|
cmd.Flags().StringArrayVarP(&opts.Volumes, "volume", "v", []string{}, "Volume. Ex: user:key@my_share:/absolute/path/to/target")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
@ -64,18 +65,17 @@ func runRun(ctx context.Context, image string, opts run.Opts) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
project, err := opts.ToContainerConfig(image)
|
containerConfig, err := opts.ToContainerConfig(image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = c.ContainerService().Run(ctx, project); err != nil {
|
if err = c.ContainerService().Run(ctx, containerConfig); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println(opts.Name)
|
fmt.Println(opts.Name)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRandomName() string {
|
func getRandomName() string {
|
||||||
|
|
|
@ -15,6 +15,7 @@ type Opts struct {
|
||||||
Name string
|
Name string
|
||||||
Publish []string
|
Publish []string
|
||||||
Labels []string
|
Labels []string
|
||||||
|
Volumes []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToContainerConfig convert run options to a container configuration
|
// ToContainerConfig convert run options to a container configuration
|
||||||
|
|
|
@ -26,7 +26,7 @@ type Port struct {
|
||||||
HostPort uint32
|
HostPort uint32
|
||||||
// ContainerPort is the port number inside the container
|
// ContainerPort is the port number inside the container
|
||||||
ContainerPort uint32
|
ContainerPort uint32
|
||||||
/// Protocol is the protocol of the port mapping
|
// Protocol is the protocol of the port mapping
|
||||||
Protocol string
|
Protocol string
|
||||||
// HostIP is the host ip to use
|
// HostIP is the host ip to use
|
||||||
HostIP string
|
HostIP string
|
||||||
|
@ -42,6 +42,8 @@ type ContainerConfig struct {
|
||||||
Ports []Port
|
Ports []Port
|
||||||
// Labels set labels to the container
|
// Labels set labels to the container
|
||||||
Labels map[string]string
|
Labels map[string]string
|
||||||
|
// Volumes to be mounted
|
||||||
|
Volumes []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogsRequest contains configuration about a log request
|
// LogsRequest contains configuration about a log request
|
||||||
|
|
Loading…
Reference in New Issue